/*
 * Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you 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.
 */
package org.wso2.carbon.identity.provider.openid.handlers;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openid4java.message.DirectError;
import org.openid4java.message.ParameterList;
import org.owasp.encoder.Encode;
import org.wso2.carbon.identity.application.authentication.framework.cache.AuthenticationRequestCacheEntry;
import org.wso2.carbon.identity.application.authentication.framework.cache.AuthenticationResultCache;
import org.wso2.carbon.identity.application.authentication.framework.cache.AuthenticationResultCacheEntry;
import org.wso2.carbon.identity.application.authentication.framework.cache.AuthenticationResultCacheKey;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticationRequest;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticationResult;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.identity.application.common.cache.CacheEntry;
import org.wso2.carbon.identity.application.common.model.ClaimMapping;
import org.wso2.carbon.identity.base.IdentityConstants;
import org.wso2.carbon.identity.base.IdentityConstants.OpenId;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.provider.IdentityProviderException;
import org.wso2.carbon.identity.provider.dto.OpenIDAuthRequestDTO;
import org.wso2.carbon.identity.provider.dto.OpenIDAuthResponseDTO;
import org.wso2.carbon.identity.provider.dto.OpenIDParameterDTO;
import org.wso2.carbon.identity.provider.openid.OpenIDConstants;
import org.wso2.carbon.identity.provider.openid.client.OpenIDAdminClient;
import org.wso2.carbon.identity.provider.openid.util.OpenIDUtil;
import org.wso2.carbon.registry.core.utils.UUIDGenerator;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.Map;

/**
 * Handles functionality related OpenID association,
 * authentication,checkid_immediate checkid_setup. check_authentication [POST] :
 * Ask an Identity Provider if a message is valid. For dumb, state-less
 * Consumers or when verifying an invalidate_handle response. checkid_setup
 * [GET] : Ask an Identity Provider if a End User owns the Claimed Identifier,
 * but be willing to wait for the reply. The Consumer will pass the User-Agent
 * to the Identity Provider for a short period of time which will return either
 * a "yes" or "cancel" answer. checkid_immediate [GET] : Ask an Identity
 * Provider if a End User owns the Claimed Identifier, getting back an immediate
 * "yes" or "can't say" answer. associate [POST] : Establish a shared secret
 * between Consumer and Identity Provider
 */
public class OpenIDHandler {

    private static final String TRUE = "true";
    // Guaranteed to be thread safe
    private static OpenIDHandler provider;
    private static final Log log = LogFactory.getLog(OpenIDHandler.class);
    private String frontEndUrl;
    private String opAddress;

    /**
     * Configure the OpenID Provider's end-point URL
     *
     * @param serverUrl
     */
    private OpenIDHandler(String serverUrl) {
        opAddress = serverUrl;
    }

    /**
     * @param serverUrl
     * @return
     */
    public static OpenIDHandler getInstance(String serverUrl) {
        if (provider == null) {
            provider = new OpenIDHandler(serverUrl);
        }
        return provider;
    }

    /**
     * This is the page the user will be redirected for authentication.
     *
     * @param frontEndUrl Authentication page
     */
    public void setFrontEndUrl(String frontEndUrl) {
        this.frontEndUrl = frontEndUrl;
        if (log.isDebugEnabled()) {
            log.debug("Authentication page set to :" + this.frontEndUrl);
        }
    }

    /**
     * @return OpenID Provider server URL.
     */
    public String getOpAddress() {
        return opAddress;
    }

    /**
     * Process the request message and handles the logic
     *
     * @param request
     * @param response
     * @return
     * @throws IdentityException
     */
    public String processRequest(HttpServletRequest request, HttpServletResponse response)
            throws IdentityException {
        // check if a logout request
        if (request.getParameter(OpenIDConstants.RequestParameter.LOGOUT_URL) != null) {
            return handleSingleLogout(request, response);
        }

        // check if a request from the login page
        if (request.getAttribute(OpenIDConstants.RequestParameter.NON_LOGIN) == null && request.getParameter
                (OpenIDConstants.RequestParameter.SESSION_DATA_KEY) != null) {
            handleRequestFromLoginPage(request, response);

        } else { // this is a request from the client or from the consent page

            // if a request from the consent page
            String approval = request.getParameter(OpenIDConstants.RequestParameter.HAS_APPROVED_ALWAYS);
            if (approval != null) {
                request.getSession().setAttribute(OpenIDConstants.SessionAttribute.USER_APPROVED_ALWAYS, approval);
                request.getSession().setAttribute(OpenIDConstants.SessionAttribute.USER_APPROVED, "true");
            }

            String responseText = null;
            try {

                HttpSession session = request.getSession();
                OpenIDAdminClient client = OpenIDUtil.getOpenIDAdminClient(session);
                ParameterList paramList = getParameterList(request);
                String mode = getOpenIDMessageMode(paramList, response, request);

                if (OpenId.ASSOCIATE.equals(mode)) {
                    // association request from the client
                    OpenIDParameterDTO[] reqDTO = OpenIDUtil.getOpenIDAuthRequest(request);
                    responseText = client.getOpenIDAssociationResponse(reqDTO);
                    if (log.isDebugEnabled()) {
                        log.debug("Association created successfully");
                    }

                } else if (OpenId.CHECKID_SETUP.equals(mode) || OpenId.CHECKID_IMMEDIATE.equals(mode)) {
                    return checkSetupOrImmediate(request, response, paramList, client);

                } else if (OpenId.CHECK_AUTHENTICATION.equals(mode)) {
                    responseText = client.verify(OpenIDUtil.getOpenIDAuthRequest(request));
                    if (log.isDebugEnabled()) {
                        log.debug("Authentication verified successfully");
                    }

                } else {
                    responseText = getErrorResponseText("Not a valid OpenID request");
                    if (log.isDebugEnabled()) {
                        log.debug("No valid MODE found : " + request.getQueryString());
                    }
                }

            } catch (Exception e) {
                log.error("Failed to process OpenID client request", e);
                responseText = getErrorResponseText(e.getMessage());
            }

            try { // Return the result to the user.
                directResponse(response, responseText);
            } catch (IOException e) {
                log.error("Failed to redirect OpenID response", e);
                throw new IdentityException("OpenID redirect reponse failed");
            }
        }

        return null;
    }

    /**
     * Returns the mode field of the OpenID message.
     *
     * @param paramList
     * @param response
     * @param request
     * @return
     * @throws IOException
     */
    private String getOpenIDMessageMode(ParameterList paramList, HttpServletResponse response,
                                        HttpServletRequest request) throws IOException {
        String mode = null;
        if (paramList == null) {
            if (log.isDebugEnabled()) {
                log.debug("Invalid OpenID message :" + request.getQueryString());
            }
            directResponse(response, getErrorResponseText("Invalid OpenID message"));
            return null;
        }
        mode = paramList.hasParameter(OpenId.ATTR_MODE) ? paramList.getParameterValue(OpenId.ATTR_MODE) : null;
        if (log.isDebugEnabled()) {
            log.debug("OpenID authentication mode :" + mode);
        }
        return mode;
    }

    /**
     * This method returns the OpenID ParameterList object.
     * If the first request, then list is taken from the http request else will
     * be taken from the http session.
     *
     * @param request
     * @return {@link ParameterList}
     */
    private ParameterList getParameterList(HttpServletRequest request) {

        if (OpenId.AUTHENTICATED.equals(request.getSession().getAttribute(OpenId.ACTION)) ||
            OpenId.CANCEL.equals(request.getSession().getAttribute(OpenId.ACTION))) {
            // not the first visit, get from the session
            return (ParameterList) request.getSession().getAttribute(OpenId.PARAM_LIST);

        } else {
            // its the fist visit, get from the request
            return new ParameterList(request.getParameterMap());
        }
    }

    /**
     * This method handles single logout requests.
     * Basically we are invalidating the session data and cookies.
     *
     * @param request
     * @param response
     * @return
     */
    private String handleSingleLogout(HttpServletRequest request, HttpServletResponse response) {

        log.info("OpenID Single Logout for " + request.getSession().getAttribute("authenticatedOpenID"));

        // removing the authenticated user from the session
        request.getSession().setAttribute("authenticatedOpenID", null);

        // removing the remember-me cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            Cookie curCookie = null;
            for (int x = 0; x < cookies.length; x++) {
                curCookie = cookies[x];
                if ("openidtoken".equalsIgnoreCase(curCookie.getName())) {
                    curCookie.setMaxAge(0);// removing the cookie
                    response.addCookie(curCookie);
                    break;
                }
            }
        }
        // TODO : Must invalidate the cookie from backend too
        return request.getParameter("logoutUrl");
    }

    /**
     * checkid_immediate : Ask an Identity Provider if an End User owns the
     * Claimed Identifier, getting back an immediate "yes" or "can't say"
     * answer. checkid_setup Description: Ask an Identity Provider if a End User
     * owns the Claimed Identifier, but be willing to wait for the reply. The
     * Consumer will pass the User-Agent to the Identity Provider for a short
     * period of time which will return either a "yes" or "cancel" answer.
     *
     * @param request
     * @param params
     * @param client
     * @throws IdentityProviderException
     */
    private String checkSetupOrImmediate(HttpServletRequest request, HttpServletResponse response, ParameterList params,
                                         OpenIDAdminClient client) throws IdentityProviderException {
        boolean authenticated = false;
        String profileName = null;
        HttpSession session = request.getSession();

        String claimedID = params.getParameterValue(IdentityConstants.OpenId.ATTR_IDENTITY);
        if (claimedID == null) {
            throw new IdentityProviderException("Required attribute openid.identity is missing");
        }

        if (claimedID.endsWith("/openid/")) {
            String openIdInSession = (String) session.getAttribute(OpenIDConstants.SessionAttribute.OPENID);
            if (openIdInSession != null && !"".equals(openIdInSession.trim())) {
                claimedID = openIdInSession;
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("Authentication check for user " + claimedID);
        }

        boolean authCompleted =
                IdentityConstants.OpenId.AUTHENTICATED.equals(session.getAttribute(IdentityConstants.OpenId.ACTION));
        boolean approved = TRUE.equals(session.getAttribute(IdentityConstants.USER_APPROVED));

        if (authCompleted && approved) {
            session.removeAttribute(IdentityConstants.USER_APPROVED);
            session.removeAttribute(IdentityConstants.OpenId.ACTION);
            session.removeAttribute(OpenIDConstants.SessionAttribute.PROFILE);
            authenticated = true;
            if (log.isDebugEnabled()) {
                log.debug("Authenticated and user confirmed :" + claimedID);
            }
            profileName = (String) session.getAttribute(OpenIDConstants.SessionAttribute.PROFILE);
            if (profileName == null) {
                profileName = OpenIDConstants.SessionAttribute.DEFAULT_PROFILE;
            }
            if (log.isDebugEnabled()) {
                log.debug("Selected profile : " + profileName);
            }
            updateRPInfo(claimedID, profileName, params, client, session);
        }

        if (IdentityConstants.OpenId.CANCEL.equals(session.getAttribute(IdentityConstants.OpenId.ACTION))) {
            if (log.isDebugEnabled()) {
                log.debug("User cancelled :" + claimedID);
            }
            authenticated = false;
        } else if (!authenticated) {
            if (log.isDebugEnabled()) {
                log.debug(claimedID + " not authenticated. Redirecting for authentication");
            }

            /*
             * We are setting the openid request parameters to the session. This
             * can be used after we are comming back from the authentication
             * page.
             */
            session.setAttribute(IdentityConstants.OpenId.PARAM_LIST, params);

            try {
                return getLoginPageUrl(claimedID, request, params);
            } catch (IdentityException | IOException e) {
                throw new IdentityProviderException("Failed to get login page url", e);
            }
        }

        OpenIDAuthRequestDTO openIDAuthRequest = new OpenIDAuthRequestDTO();

        if (IdentityConstants.TRUE.equals(session.getAttribute(IdentityConstants.PHISHING_RESISTANCE))) {
            openIDAuthRequest.setPhishiingResistanceAuthRequest(true);
            // Clear the session.
            session.removeAttribute(IdentityConstants.PHISHING_RESISTANCE);
        }
        if (IdentityConstants.TRUE.equals(session.getAttribute(IdentityConstants.MULTI_FACTOR_AUTH))) {
            openIDAuthRequest.setMultiFactorAuthRequested(true);
            // Clear the cache.
            session.removeAttribute(IdentityConstants.MULTI_FACTOR_AUTH);
        }
        openIDAuthRequest.setParams(OpenIDUtil.getOpenIDAuthRequest(params));
        openIDAuthRequest.setOpLocalId(claimedID);
        openIDAuthRequest.setUserSelectedClaimedId(claimedID);
        openIDAuthRequest.setAuthenticated(authenticated);
        openIDAuthRequest.setOpenID(claimedID);
        openIDAuthRequest.setProfileName(profileName);

        String authenticatedIdPsParam = "";

        // setting the user claims received from the framework
        if (session.getAttribute(OpenIDConstants.AUTHENTICATION_RESULT) != null) {
            AuthenticationResult authResult =
                    (AuthenticationResult) session.getAttribute(OpenIDConstants.AUTHENTICATION_RESULT);
            openIDAuthRequest.setResponseClaims(authResult.getSubject().getUserAttributes());
            String authenticatedIdPs = authResult.getAuthenticatedIdPs();

            if (authenticatedIdPs != null && !authenticatedIdPs.isEmpty()) {
                try {
                    authenticatedIdPsParam = "&AuthenticatedIdPs=" + URLEncoder.encode(authenticatedIdPs, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new IdentityProviderException("Url encoding failed", e);
                }
            }
        }

        session.removeAttribute(OpenIDConstants.SessionAttribute.OPENID);
        OpenIDAuthResponseDTO openIDAuthResponse = client.getOpenIDAuthResponse(openIDAuthRequest);

        if (openIDAuthResponse != null) {
            return openIDAuthResponse.getDestinationUrl() + authenticatedIdPsParam;
        }
        return null;
    }

    /**
     * Updates the RP information of user in the database
     *
     * @param openId
     * @param profileName
     * @param params
     * @param client
     * @param
     * @param session
     * @throws IdentityProviderException
     */
    private void updateRPInfo(String openId, String profileName, ParameterList params,
                              OpenIDAdminClient client, HttpSession session) throws IdentityProviderException {

        if (!client.isOpenIDUserApprovalBypassEnabled()) {

            boolean alwaysApprovedRp = Boolean.parseBoolean((String) session.getAttribute(
                    OpenIDConstants.SessionAttribute.USER_APPROVED_ALWAYS));

            client.updateOpenIDUserRPInfo(params.getParameterValue(IdentityConstants.OpenId.ATTR_RETURN_TO),
                                          alwaysApprovedRp, profileName, openId);

        }
    }

    /**
     * Returns the login page URL. User will be redirected to this URL when they
     * are not authenticated.
     *
     * @param claimedID
     * @param request
     * @param params
     * @return loginPageUrl
     * @throws IdentityException
     * @throws IOException
     */
    private String getLoginPageUrl(String claimedID, HttpServletRequest request, ParameterList params)
            throws IdentityException, IOException {
            
        /*
         * We are setting the request's openid identifier to the session
         * here.  
         */
        request.getSession().setAttribute(OpenIDConstants.SessionAttribute.OPENID, claimedID);

        String commonAuthURL = IdentityUtil.getServerURL(FrameworkConstants.COMMONAUTH, true);
        String selfPath = request.getContextPath();
        String sessionDataKey = UUIDGenerator.generateUUID();

        //Authentication context keeps data which should be sent to commonAuth endpoint
        AuthenticationRequest authenticationRequest = new
                AuthenticationRequest();
        authenticationRequest.setRelyingParty(getRelyingParty(request));
        authenticationRequest.setCommonAuthCallerPath(selfPath);
        String username = null;
        String tenantDomain = null;
        if (params.getParameterValue(FrameworkConstants.OPENID_IDENTITY) != null) {
            username = OpenIDUtil.getUserName(params.getParameterValue(FrameworkConstants.OPENID_IDENTITY));
            authenticationRequest.addRequestQueryParam(FrameworkConstants.USERNAME, new String[] { username });
        }
        if (params.getParameterValue(FrameworkConstants.RequestParams.TENANT_DOMAIN) != null) {
            tenantDomain = params.getParameterValue(FrameworkConstants.RequestParams.TENANT_DOMAIN);
            authenticationRequest.setTenantDomain(tenantDomain);
        }

        boolean forceAuthenticate = false;
        if (!claimedID.endsWith("/openid/")) {
            String authenticatedUser =
                    (String) request.getSession().getAttribute(OpenIDConstants.SessionAttribute.AUTHENTICATED_OPENID);
            if (log.isDebugEnabled()) {
                log.debug("claimedID : " + claimedID + ", authenticated user : " + authenticatedUser);
            }
            if (authenticatedUser != null && !"".equals(authenticatedUser.trim())
                && !claimedID.equals(authenticatedUser.trim())) {
                if (log.isDebugEnabled()) {
                    log.debug("Overriding previously authenticated OpenID : " + authenticatedUser
                              + " with the OpenID in the current request :" + claimedID
                              + " and setting forceAuthenticate.");
                }
                forceAuthenticate = true;
            }
        }
        authenticationRequest.setForceAuth(forceAuthenticate);
        //Add request headers to authentication request context. ie to cache
        authenticationRequest.setRequestQueryParams(request.getParameterMap());
        for (Enumeration headerNames = request.getHeaderNames(); headerNames.hasMoreElements(); ) {
            String headerName = headerNames.nextElement().toString();
            authenticationRequest.addHeader(headerName, request.getHeader(headerName));
        }

        AuthenticationRequestCacheEntry authRequest = new AuthenticationRequestCacheEntry(authenticationRequest);
        FrameworkUtils.addAuthenticationRequestToCache(sessionDataKey, authRequest);
        StringBuilder queryStringBuilder = new StringBuilder();
        queryStringBuilder.append(commonAuthURL).
                append("?").
                                  append(FrameworkConstants.SESSION_DATA_KEY).
                                  append("=").
                                  append(sessionDataKey).
                                  append("&").
                                  append(FrameworkConstants.RequestParams.TYPE).
                                  append("=").
                                  append(FrameworkConstants.RequestType.CLAIM_TYPE_OPENID);
        // reading the authorization header for request path authentication
        FrameworkUtils.setRequestPathCredentials(request);

        return queryStringBuilder.toString();
    }

    /**
     * Return the error response message based on the given message
     *
     * @param message Error message
     * @return Direct error
     */
    private String getErrorResponseText(String message) {
        log.error(message);
        return DirectError.createDirectError(message).keyValueFormEncoding();
    }

    /**
     * Send a direct response to the RP.
     *
     * @param httpResp HttpServletResponse
     * @param response Response message
     * @return
     * @throws IOException
     */
    private void directResponse(HttpServletResponse httpResp, String response) throws IOException {
        ServletOutputStream stream = null;
        try {
            stream = httpResp.getOutputStream();
            stream.write(response.getBytes());
        } finally {
            if (stream != null) {
                stream.close();
            }
        }
    }

    private void handleRequestFromLoginPage(HttpServletRequest req, HttpServletResponse resp) throws IdentityException {

        try {
            HttpSession session = req.getSession();

            String claimedID = (String) session.getAttribute(OpenIDConstants.RequestParameter.OPENID);

            if (claimedID == null) {
                throw new IdentityException("No valid OpenID Identifier found. Terminating authentication flow");
            }

            String userName = null;
            AuthenticationResult authnResult = null;

            if (req.getParameter("sessionDataKey") != null) {
                authnResult = getAuthenticationResultFromCache(req.getParameter("sessionDataKey"));
            }

            // Directed Identity handling
            if (claimedID.endsWith("/openid/")) {

                if (authnResult != null && authnResult.isAuthenticated()) {
                    userName = authnResult.getSubject().getAuthenticatedSubjectIdentifier();
                    session.setAttribute(OpenIDConstants.AUTHENTICATION_RESULT, authnResult);
                }

                String authenticatedUserName = (String) session.getAttribute(OpenIDConstants.SessionAttribute.USERNAME);

                // setting the authenticated user in the session
                if (userName != null && !"".equals(userName.trim())) {

                    if (authenticatedUserName != null && authenticatedUserName.equals(userName)) {
                        log.debug(
                                "Username in request is different from the authenticated username in the session. Starting new session ");
                        session.removeAttribute(OpenIDConstants.SessionAttribute.USERNAME);
                    }

                    session.setAttribute(OpenIDConstants.SessionAttribute.USERNAME, userName);
                    claimedID = claimedID + userName;
                }

                // set the username from the session
                if (userName == null || "".equals(userName.trim())) {
                    userName = authenticatedUserName;
                }
            }

            // This is important. Created openid in the directed identity case
            session.setAttribute(OpenIDConstants.SessionAttribute.OPENID, claimedID);

            boolean isAuthenticated = false;

            // if the user is already authenticated, then the openid should be in the session
            String authenticatedOpenID =
                    (String) session.getAttribute(OpenIDConstants.SessionAttribute.AUTHENTICATED_OPENID);

            // if they are the same, then user is already logged in
            if (authenticatedOpenID != null && authenticatedOpenID.equals(claimedID)) {
                isAuthenticated = true;
            }

            OpenIDAdminClient client = OpenIDUtil.getOpenIDAdminClient(session);

            if (!isAuthenticated && authnResult != null) {
                isAuthenticated = authnResult.isAuthenticated();
            }

            if (isAuthenticated) {

                // set the authenticated openid identifier in the session
                session.setAttribute(OpenIDConstants.SessionAttribute.AUTHENTICATED_OPENID, claimedID);

                // check for the <OpenIDSkipUserConsent> in the identity.xml
                if (client.isOpenIDUserApprovalBypassEnabled()) {

                    session.setAttribute(OpenIDConstants.SessionAttribute.ACTION,
                                         IdentityConstants.OpenId.AUTHENTICATED);
                    session.setAttribute(OpenIDConstants.SessionAttribute.USER_APPROVED, "true");
                    session.setAttribute(OpenIDConstants.SessionAttribute.SELECTED_PROFILE, "default");

                    req.setAttribute("nonlogin", "true");
                    RequestDispatcher dispatcher = req.getRequestDispatcher("../../openidserver");
                    dispatcher.forward(req, resp);

                } else {
                    // now we read the database for the user's consent.
                    String[] rpInfo = client.getOpenIDUserRPInfo(claimedID, ((ParameterList) session
                            .getAttribute(OpenId.PARAM_LIST)).getParameterValue(OpenId.ATTR_RETURN_TO));

                    if ("true".equals(rpInfo[0])) { // approve always
                        session.setAttribute(OpenIDConstants.SessionAttribute.ACTION,
                                             IdentityConstants.OpenId.AUTHENTICATED);
                        session.setAttribute(OpenIDConstants.SessionAttribute.USER_APPROVED, "true");
                        session.setAttribute(OpenIDConstants.SessionAttribute.USER_APPROVED_ALWAYS, "true");
                        session.setAttribute(OpenIDConstants.SessionAttribute.SELECTED_PROFILE, rpInfo[1]);

                        req.setAttribute("nonlogin", "true");
                        RequestDispatcher dispatcher = req.getRequestDispatcher("../../openidserver");
                        dispatcher.forward(req, resp);

                    } else {
                        // no approval found of the user, so ask for it.

                        session.setAttribute(OpenIDConstants.SessionAttribute.ACTION,
                                             IdentityConstants.OpenId.AUTHENTICATED);
                        sendToApprovalPage(req, resp);
                    }
                }

            } else {
                // re-login the user
                OpenIDUtil.deleteCookie("openidtoken", "/", req);
                OpenIDUtil.deleteCookie("openidrememberme", "/", req);
                session.removeAttribute(OpenIDConstants.SessionAttribute.AUTHENTICATED_OPENID);

                // sending to the login page
                String openIdEndpointUrl = org.wso2.carbon.identity.provider.openid.OpenIDUtil.getOpenIDLoginPageURL();

                resp.sendRedirect(openIdEndpointUrl
                                  + OpenIDUtil.getLoginPageQueryParams((ParameterList) req.getSession().getAttribute(
                        OpenId.PARAM_LIST)) + "&errorMsg=error.while.user.auth");
            }
        } catch (Exception e) {
            throw new IdentityException("Exception while handling request from the login page", e);
        }
    }

    private void sendToApprovalPage(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        HttpSession session = req.getSession();

        PrintWriter out = resp.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<p>You are now redirected back to Approval Page.");
        out.println(" If the redirection fails, please click the post button.</p>");
        out.println("<form method='post' action='authenticationendpoint/openid_profile.do'>");
        out.println("<p>");

        AuthenticationResult authnResult = null;

        if (req.getParameter("sessionDataKey") != null) {
            authnResult = getAuthenticationResultFromCache(req.getParameter("sessionDataKey"));
        }

        Map<ClaimMapping, String> userAttributes = authnResult.getSubject().getUserAttributes();

        out.println("<input type='hidden' name='openid.identity' value='" +
                Encode.forHtmlAttribute((String) session.getAttribute
                        (OpenIDConstants.SessionAttribute.AUTHENTICATED_OPENID)) + "'>");
        out.println("<input type='hidden' name='openid.return_to' value='" +
                Encode.forHtmlAttribute(((ParameterList) session.getAttribute(OpenId.PARAM_LIST)).
                        getParameterValue(OpenId.ATTR_RETURN_TO)) + "'>");

        if (userAttributes != null) {
            for (ClaimMapping claimMapping : userAttributes.keySet()) {
                String value = userAttributes.get(claimMapping);
                if (value != null) {
                    out.println("<input type='hidden' name='claimTag' value='" +
                            Encode.forHtmlAttribute(claimMapping.getLocalClaim().getClaimUri()) + "'>");
                    out.println(
                            "<input type='hidden' name='claimValue' value='" +
                                    Encode.forHtmlAttribute(userAttributes.get(claimMapping)) + "'>");
                }
            }
        }

        out.println("<button type='submit'>POST</button>");
        out.println("</p>");
        out.println("</form>");
        out.println("<script type='text/javascript'>");
        out.println("document.forms[0].submit();");
        out.println("</script>");
        out.println("</body>");
        out.println("</html>");

        return;
    }

    private AuthenticationResult getAuthenticationResultFromCache(String sessionDataKey) {
        AuthenticationResult authResult = null;
        AuthenticationResultCacheEntry authResultCacheEntry = FrameworkUtils.getAuthenticationResultFromCache(sessionDataKey);
        if (authResultCacheEntry != null) {
            authResult = authResultCacheEntry.getResult();
        } else {
            log.error("Cannot find AuthenticationResult from the cache");
        }

        return authResult;
    }

    private String getRelyingParty(HttpServletRequest request) {
        return request.getParameter("openid.realm");
    }
}
