WIP: Asgardeo Login Issue #57

Draft
deenath wants to merge 4 commits from deenath/device-mgt-core:asgardeo_logout_issue into master

@ -0,0 +1,60 @@
/*
* Copyright (c) 2023, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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 io.entgra.ui.request.interceptor;
import com.nimbusds.jwt.SignedJWT;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.http.HttpSession;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
public class SessionIdStore {
private static Log log = LogFactory.getLog(SessionIdStore.class);
private static Map<String, HttpSession> sessionMap = new HashMap();
public SessionIdStore() {
}
public static void storeSession(String sid, HttpSession session) {
log.info("Storing session: " + session.getId() + " against the sid: " + sid);
sessionMap.put(sid, session);
}
public static String getSid(String idToken) throws ParseException {
String sid = (String) SignedJWT.parse(idToken).getJWTClaimsSet().getClaim("sid");
return sid;
}
public static HttpSession getSession(String sid) {
if (sid != null && sessionMap.get(sid) != null) {
log.info("Retrieving session: " + ((HttpSession) sessionMap.get(sid)).getId() + " for the sid: " + sid);
return (HttpSession) sessionMap.get(sid);
} else {
log.error("No session found for the sid: " + sid);
return null;
}
}
public static void removeSession(String sid) {
sessionMap.remove(sid);
}
}

@ -22,6 +22,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.entgra.ui.request.interceptor.beans.AuthData;
import io.entgra.ui.request.interceptor.beans.ProxyResponse;
import io.entgra.ui.request.interceptor.util.HandlerConstants;
import io.entgra.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.logging.Log;
@ -30,7 +31,6 @@ import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import io.entgra.ui.request.interceptor.beans.ProxyResponse;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
@ -39,6 +39,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.text.ParseException;
@MultipartConfig
@WebServlet("/ssoLoginCallback")
@ -92,8 +93,20 @@ public class SsoLoginCallbackHandler extends HttpServlet {
authData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString());
authData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString());
authData.setScope(jTokenResultAsJsonObject.get("scope").getAsString());
String idToken = (jTokenResultAsJsonObject.get("id_token").getAsString());
authData.setIdToken(idToken);
session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, authData);
try {
SessionIdStore.storeSession(SessionIdStore.getSid(idToken), session);
} catch (ParseException e) {
log.error("Error while obtaining sid from id_token", e);
}
resp.sendRedirect(session.getAttribute("redirectUrl").toString());
}
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
this.doGet(req, resp);
}
}

@ -18,11 +18,8 @@
package io.entgra.ui.request.interceptor;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.google.gson.*;
import io.entgra.ui.request.interceptor.beans.ProxyResponse;
import io.entgra.ui.request.interceptor.cache.LoginCache;
import io.entgra.ui.request.interceptor.cache.OAuthApp;
import io.entgra.ui.request.interceptor.cache.OAuthAppCacheKey;
@ -43,7 +40,6 @@ import org.json.JSONArray;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import io.entgra.ui.request.interceptor.beans.ProxyResponse;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@ -248,6 +244,9 @@ public class SsoLoginHandler extends HttpServlet {
return;
}
HandlerUtil.handleError(resp, null);
// Enables BackChannelLogout
enableBackChannelLogout();
} catch (IOException e) {
log.error("Error occurred while sending the response into the socket. ", e);
} catch (JsonSyntaxException e) {
@ -312,7 +311,17 @@ public class SsoLoginHandler extends HttpServlet {
HandlerConstants.JWT_BEARER_GRANT_TYPE
})
);
jsonObject.put(HandlerConstants.CALLBACK_URL_KEY, iotsCoreUrl + baseContextPath + HandlerConstants.SSO_LOGIN_CALLBACK);
String logoutRedirect = "";
if (applicationName.equals("entgra")) {
logoutRedirect = iotsCoreUrl + "/endpoint-mgt";
} else if (applicationName.equals("publisher")) {
logoutRedirect = iotsCoreUrl + "/app-publisher";
}
else{
logoutRedirect = (iotsCoreUrl + "/" + applicationName);
}
jsonObject.put(HandlerConstants.CALLBACK_URL_KEY, "regexp=(" + iotsCoreUrl + baseContextPath
+ HandlerConstants.SSO_LOGIN_CALLBACK + "|" + logoutRedirect + ")");
String payload = jsonObject.toString();
return new StringEntity(payload, ContentType.APPLICATION_JSON);
}
@ -468,4 +477,24 @@ public class SsoLoginHandler extends HttpServlet {
HandlerUtil.execute(updateApplicationEndpoint);
}
/***
* Enables Backchannel Logout
* This Invokes the Identity server and updates its specific application with logoutCallBackHandler URL
*/
private void enableBackChannelLogout() throws IOException {
String apiUpdateOAuth = iotsCoreUrl + HandlerConstants.IDENTITY_DCR_ENDPOINT + oAuthApp.getClientId();
HttpPut setBackChannelLogout = new HttpPut(apiUpdateOAuth);
setBackChannelLogout.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC +
encodedAdminCredentials);
setBackChannelLogout.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty(HandlerConstants.BACKCHANNEL_LOGOUT_URI, iotsCoreUrl + baseContextPath
+ HandlerConstants.SSO_LOGOUT_CALLBACK);
jsonObject.addProperty(HandlerConstants.BACKCHANNEL_LOGOUT_SESSION_REQUIRED, true);
String payload = jsonObject.toString();
setBackChannelLogout.setEntity(new StringEntity(payload, ContentType.APPLICATION_JSON));
HandlerUtil.execute(setBackChannelLogout);
}
}

@ -0,0 +1,62 @@
/*
* Copyright (c) 2023, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
*
* Entgra (Pvt) Ltd. 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 io.entgra.ui.request.interceptor;
import com.nimbusds.jwt.SignedJWT;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.text.ParseException;
@MultipartConfig
@WebServlet("/ssoLogoutCallback")
public class SsoLogoutCallbackHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(SsoLogoutCallbackHandler.class);
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
this.doPost(req, resp);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) {
log.info("BackChannel Logout Invoked");
try {
String sid = (String) SignedJWT.parse(req.getParameter("logout_token")).getJWTClaimsSet().getClaim("sid");
HttpSession session = SessionIdStore.getSession(sid);
if (session != null) {
session.invalidate();
SessionIdStore.removeSession(sid);
log.info("Session invalidated successfully for sid: " + sid);
} else {
log.info("Cannot find corresponding session for sid: " + sid);
}
} catch (ParseException e) {
log.error("Error in generating Logout Token.", e);
}
}
}

@ -18,12 +18,13 @@
package io.entgra.ui.request.interceptor;
import io.entgra.ui.request.interceptor.beans.AuthData;
import io.entgra.ui.request.interceptor.beans.ProxyResponse;
import io.entgra.ui.request.interceptor.util.HandlerConstants;
import io.entgra.ui.request.interceptor.util.HandlerUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpStatus;
import io.entgra.ui.request.interceptor.beans.ProxyResponse;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
@ -31,39 +32,48 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.Cookie;
import java.io.IOException;
@MultipartConfig
@WebServlet("/ssoLogout")
public class SsoLogoutHandler extends HttpServlet {
private static final Log log = LogFactory.getLog(SsoLogoutHandler.class);
private static Log log = LogFactory.getLog(SsoLogoutHandler.class);
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
for (String path : HandlerConstants.SSO_LOGOUT_COOKIE_PATHS) {
removeCookie(HandlerConstants.JSESSIONID_KEY, path, resp);
}
removeCookie(HandlerConstants.COMMON_AUTH_ID_KEY, "/", resp);
ProxyResponse proxyResponse = new ProxyResponse();
proxyResponse.setStatus(ProxyResponse.Status.SUCCESS);
proxyResponse.setCode(HttpStatus.SC_OK);
String iotsCoreUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR)
+ HandlerConstants.COLON + HandlerUtil.getCorePort(req.getScheme());
String baseContextPath = req.getContextPath();
String applicationName = baseContextPath.substring(1, baseContextPath.indexOf("-ui-request-handler"));
String logoutRedirect = "";
if (applicationName.equals("entgra")) {
logoutRedirect = iotsCoreUrl + "/endpoint-mgt";
} else if(applicationName.equals("publisher")) {
logoutRedirect = iotsCoreUrl + "/app-publisher";
}
else{
logoutRedirect = (iotsCoreUrl + "/" + applicationName);
}
HttpSession session = req.getSession(false);
AuthData authData = null;
if (session != null) {
session.invalidate();
authData = (AuthData) session.getAttribute("authInfo");
}
try {
HandlerUtil.handleSuccess(resp, proxyResponse);
String redirect = iotsCoreUrl + "/oidc/logout" +
"?id_token_hint=" + authData.getIdToken() +
"&post_logout_redirect_uri=" + logoutRedirect +
"&state=xyz";
resp.sendRedirect(redirect);
} catch (IOException e) {
log.error("Error occurred when processing logout request.", e);
log.error("Error occured while redirecting");
} catch (NullPointerException e) {
log.error("Invalid Session");
resp.setStatus(401);
}
}
private static void removeCookie(String cookieName,String path, HttpServletResponse response) {
Cookie cookie = new Cookie(cookieName, "");
cookie.setPath(path);
cookie.setValue(null);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}

@ -31,6 +31,8 @@ public class AuthData implements java.io.Serializable {
private String encodedClientApp;
private String scope;
private String idToken;
public String getAccessToken() {
return accessToken;
}
@ -86,4 +88,12 @@ public class AuthData implements java.io.Serializable {
public void setScope(String scope) {
this.scope = scope;
}
public String getIdToken() {
return idToken;
}
public void setIdToken(String idToken) {
this.idToken = idToken;
}
}

@ -30,6 +30,10 @@ public class HandlerConstants {
public static final String IDENTITY_APP_MGT_ENDPOINT = "/services/IdentityApplicationManagementService.IdentityApplicationManagementServiceHttpsSoap11Endpoint";
public static final String LOGIN_PAGE = "/login";
public static final String SSO_LOGIN_CALLBACK = "/ssoLoginCallback";
public static final String SSO_LOGOUT_CALLBACK = "/ssoLogoutCallback";
public static final String IDENTITY_DCR_ENDPOINT = "/api/identity/oauth2/dcr/v1.1/register/";
public static final String BACKCHANNEL_LOGOUT_URI = "backchannel_logout_uri";
public static final String BACKCHANNEL_LOGOUT_SESSION_REQUIRED= "backchannel_logout_session_required";
public static final String BASIC = "Basic ";
public static final String BEARER = "Bearer ";
public static final String X_FRAME_OPTIONS = "X-Frame-Options";

@ -18,12 +18,9 @@
package io.entgra.ui.request.interceptor.util;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.*;
import io.entgra.ui.request.interceptor.beans.AuthData;
import io.entgra.ui.request.interceptor.beans.ProxyResponse;
import io.entgra.ui.request.interceptor.cache.LoginCache;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
@ -54,7 +51,6 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import io.entgra.ui.request.interceptor.beans.ProxyResponse;
import org.xml.sax.SAXException;
import javax.servlet.http.HttpServletRequest;
@ -63,12 +59,7 @@ import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.*;
import java.util.Enumeration;
import java.util.List;
@ -193,7 +184,6 @@ public class HandlerUtil {
}
/***
* Handle error requests.
*
@ -652,6 +642,7 @@ public class HandlerUtil {
// handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR);
return tokenResultResponse;
}
public static ProxyResponse getTokenResult(AuthData authData, String keymanagerUrl) throws IOException {
HttpPost tokenEndpoint = new HttpPost(keymanagerUrl + HandlerConstants.OAUTH2_TOKEN_ENDPOINT);
StringEntity tokenEndpointPayload = new StringEntity(
@ -711,7 +702,7 @@ public class HandlerUtil {
public static String getHeaderValue(String headerName, Header[] headers) {
String headerValue = null;
for(Header header : headers) {
for (Header header : headers) {
if (header.getName().equalsIgnoreCase(headerName)) {
headerValue = header.getValue();
}

Loading…
Cancel
Save