Java tutorial
/******************************************************************************* * Copyright 2015, 2016 Tremolo Security, Inc. * 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. *******************************************************************************/ package com.tremolosecurity.idp.providers; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLEncoder; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.StringTokenizer; import java.util.UUID; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; import java.util.zip.ZipOutputStream; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.apache.xml.security.utils.Base64; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.cfgxml.spi.LoadedConfig; import org.hibernate.boot.jaxb.cfg.spi.JaxbCfgHibernateConfiguration; import org.hibernate.boot.jaxb.cfg.spi.JaxbCfgMappingReferenceType; import org.hibernate.boot.jaxb.cfg.spi.JaxbCfgHibernateConfiguration.JaxbCfgSessionFactory; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.Configuration; import org.joda.time.DateTime; import org.jose4j.jwe.JsonWebEncryption; import org.jose4j.jwk.JsonWebKey; import org.jose4j.jwk.JsonWebKeySet; import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.consumer.InvalidJwtException; import org.jose4j.lang.JoseException; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.novell.ldap.LDAPEntry; import com.novell.ldap.LDAPException; import com.novell.ldap.LDAPSearchResults; import com.tremolosecurity.config.util.ConfigManager; import com.tremolosecurity.config.util.UrlHolder; import com.tremolosecurity.config.xml.ApplicationType; import com.tremolosecurity.config.xml.AuthChainType; import com.tremolosecurity.idp.providers.oidc.model.OIDCSession; import com.tremolosecurity.idp.providers.oidc.model.OpenIDConnectConfig; import com.tremolosecurity.idp.providers.oidc.session.ClearOidcSessionOnLogout; import com.tremolosecurity.idp.server.IDP; import com.tremolosecurity.idp.server.IdentityProvider; import com.tremolosecurity.json.Token; import com.tremolosecurity.log.AccessLog; import com.tremolosecurity.log.AccessLog.AccessEvent; import com.tremolosecurity.provisioning.core.ProvisioningException; import com.tremolosecurity.provisioning.core.User; import com.tremolosecurity.provisioning.mapping.MapIdentity; import com.tremolosecurity.proxy.auth.AuthController; import com.tremolosecurity.proxy.auth.AuthInfo; import com.tremolosecurity.proxy.auth.AzSys; import com.tremolosecurity.proxy.auth.RequestHolder; import com.tremolosecurity.proxy.auth.passwordreset.PasswordResetRequest; import com.tremolosecurity.proxy.filter.HttpFilterChain; import com.tremolosecurity.proxy.filter.HttpFilterChainImpl; import com.tremolosecurity.proxy.filter.HttpFilterRequest; import com.tremolosecurity.proxy.filter.HttpFilterRequestImpl; import com.tremolosecurity.proxy.filter.HttpFilterResponse; import com.tremolosecurity.proxy.filter.HttpFilterResponseImpl; import com.tremolosecurity.proxy.filter.PostProcess; import com.tremolosecurity.proxy.logout.LogoutUtil; import com.tremolosecurity.proxy.util.NextSys; import com.tremolosecurity.proxy.util.ProxyConstants; import com.tremolosecurity.saml.Attribute; import com.tremolosecurity.server.GlobalEntries; import com.tremolosecurity.server.StopableThread; public class OpenIDConnectIdP implements IdentityProvider { public static final String UNISON_OPENIDCONNECT_IDPS = "unison.openidconnectidps"; static org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager .getLogger(OpenIDConnectIdP.class.getName()); private static final String TRANSACTION_DATA = "unison.openidconnect.session"; public static final String UNISON_SESSION_OIDC_ACCESS_TOKEN = "unison.session.oidc.access.token"; public static final String UNISON_SESSION_OIDC_ID_TOKEN = "unison.session.oidc.id.token"; String idpName; HashMap<String, OpenIDConnectTrust> trusts; String jwtSigningKeyName; private SessionFactory sessionFactory; private MapIdentity mapper; public void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { } public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String action = (String) request.getAttribute(IDP.ACTION_NAME); UrlHolder holder = (UrlHolder) request.getAttribute(ProxyConstants.AUTOIDM_CFG); if (holder == null) { throw new ServletException("Holder is null"); } AuthController ac = ((AuthController) request.getSession().getAttribute(ProxyConstants.AUTH_CTL)); if (action.equalsIgnoreCase(".well-known/openid-configuration")) { Gson gson = new GsonBuilder().setPrettyPrinting().create(); String json = gson.toJson(new OpenIDConnectConfig(this.idpName, request, mapper)); response.setContentType("application/json"); response.getWriter().print(json); AccessLog.log(AccessEvent.AzSuccess, holder.getApp(), (HttpServletRequest) request, ac.getAuthInfo(), "NONE"); return; } else if (action.equalsIgnoreCase("certs")) { try { X509Certificate cert = GlobalEntries.getGlobalEntries().getConfigManager() .getCertificate(this.jwtSigningKeyName); JsonWebKey jwk = JsonWebKey.Factory.newJwk(cert.getPublicKey()); String keyID = buildKID(cert); jwk.setKeyId(keyID); jwk.setUse("sig"); jwk.setAlgorithm("RS256"); response.setContentType("application/json"); response.getWriter().print(new JsonWebKeySet(jwk).toJson()); AccessLog.log(AccessEvent.AzSuccess, holder.getApp(), (HttpServletRequest) request, ac.getAuthInfo(), "NONE"); return; } catch (JoseException e) { throw new ServletException("Could not generate jwt", e); } } else if (action.equalsIgnoreCase("auth")) { String clientID = request.getParameter("client_id"); String responseCode = request.getParameter("response_type"); String scope = request.getParameter("scope"); String redirectURI = request.getParameter("redirect_uri"); String state = request.getParameter("state"); String nonce = request.getParameter("nonce"); OpenIDConnectTransaction transaction = new OpenIDConnectTransaction(); transaction.setClientID(clientID); transaction.setResponseCode(responseCode); transaction.setNonce(nonce); StringTokenizer toker = new StringTokenizer(scope, " ", false); while (toker.hasMoreTokens()) { String token = toker.nextToken(); transaction.getScope().add(token); } transaction.setRedirectURI(redirectURI); transaction.setState(state); OpenIDConnectTrust trust = trusts.get(clientID); if (trust == null) { StringBuffer b = new StringBuffer(); b.append(redirectURI).append("?error=unauthorized_client"); logger.warn("Trust '" + clientID + "' not found"); response.sendRedirect(b.toString()); return; } if (trust.isVerifyRedirect()) { if (!trust.getRedirectURI().equals(redirectURI)) { StringBuffer b = new StringBuffer(); b.append(trust.getRedirectURI()).append("?error=unauthorized_client"); logger.warn("Invalid redirect"); AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, ac.getAuthInfo(), "NONE"); response.sendRedirect(b.toString()); return; } transaction.setRedirectURI(trust.getRedirectURI()); } else { transaction.setRedirectURI(redirectURI); } if (transaction.getScope().size() == 0 || !transaction.getScope().get(0).equals("openid")) { StringBuffer b = new StringBuffer(); b.append(transaction.getRedirectURI()).append("?error=invalid_scope"); logger.warn("First scope not openid"); AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, ac.getAuthInfo(), "NONE"); response.sendRedirect(b.toString()); return; } else { //we don't need the openid scope anymore transaction.getScope().remove(0); } String authChain = trust.getAuthChain(); if (authChain == null) { StringBuffer b = new StringBuffer(); b.append("IdP does not have an authenticaiton chain configured"); throw new ServletException(b.toString()); } HttpSession session = request.getSession(); AuthInfo authData = ((AuthController) session.getAttribute(ProxyConstants.AUTH_CTL)).getAuthInfo(); AuthChainType act = holder.getConfig().getAuthChains().get(authChain); session.setAttribute(OpenIDConnectIdP.TRANSACTION_DATA, transaction); if (authData == null || !authData.isAuthComplete() && !(authData.getAuthLevel() < act.getLevel())) { nextAuth(request, response, session, false, act); } else { if (authData.getAuthLevel() < act.getLevel()) { //step up authentication, clear existing auth data session.removeAttribute(ProxyConstants.AUTH_CTL); holder.getConfig().createAnonUser(session); nextAuth(request, response, session, false, act); } else { StringBuffer b = genFinalURL(request); response.sendRedirect(b.toString()); //TODO if session already exists extend the life of the id_token } } } else if (action.contentEquals("completefed")) { this.completeFederation(request, response); } else if (action.equalsIgnoreCase("userinfo")) { try { processUserInfoRequest(request, response); } catch (JoseException | InvalidJwtException e) { throw new ServletException("Could not process userinfo request", e); } } } private String buildKID(X509Certificate cert) { StringBuffer b = new StringBuffer(); b.append(cert.getSubjectDN().getName()).append('-').append(cert.getIssuerDN().getName()).append('-') .append(cert.getSerialNumber().toString()); return b.toString(); } private boolean nextAuth(HttpServletRequest req, HttpServletResponse resp, HttpSession session, boolean jsRedirect, AuthChainType act) throws ServletException, IOException { RequestHolder reqHolder; UrlHolder holder = (UrlHolder) req.getAttribute(ProxyConstants.AUTOIDM_CFG); String urlChain = holder.getUrl().getAuthChain(); StringBuffer b = genFinalURL(req); return holder.getConfig().getAuthManager().execAuth(req, resp, session, jsRedirect, holder, act, b.toString()); } private StringBuffer genFinalURL(HttpServletRequest req) { if (logger.isDebugEnabled()) { logger.debug("url : '" + req.getRequestURL() + "'"); } ConfigManager cfg = (ConfigManager) req.getAttribute(ProxyConstants.TREMOLO_CFG_OBJ); String url = req.getRequestURL().substring(0, req.getRequestURL().indexOf("/", 8)); StringBuffer b = new StringBuffer(url); b.append(cfg.getAuthIdPPath()).append(this.idpName).append("/completefed"); if (logger.isDebugEnabled()) { logger.debug("final url : '" + b + "'"); } return b; } public void doHead(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { } public void doOptions(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { } public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String action = (String) request.getAttribute(IDP.ACTION_NAME); if (action.contentEquals("completefed")) { this.completeFederation(request, response); } else if (action.equalsIgnoreCase("token")) { String code = request.getParameter("code"); String clientID = request.getParameter("client_id"); String clientSecret = request.getParameter("client_secret"); String redirectURI = request.getParameter("redirect_uri"); String grantType = request.getParameter("grant_type"); String refreshToken = request.getParameter("refresh_token"); if (clientID == null) { //this means that the clientid is in the Authorization header String azHeader = request.getHeader("Authorization"); azHeader = azHeader.substring(azHeader.indexOf(' ') + 1).trim(); azHeader = new String(org.apache.commons.codec.binary.Base64.decodeBase64(azHeader)); clientID = azHeader.substring(0, azHeader.indexOf(':')); clientSecret = azHeader.substring(azHeader.indexOf(':') + 1); } AuthController ac = (AuthController) request.getSession().getAttribute(ProxyConstants.AUTH_CTL); UrlHolder holder = (UrlHolder) request.getAttribute(ProxyConstants.AUTOIDM_CFG); holder.getApp().getCookieConfig().getTimeout(); if (refreshToken != null) { try { refreshToken(response, clientID, clientSecret, refreshToken, holder, request, ac.getAuthInfo()); } catch (Exception e) { throw new ServletException("Could not refresh token", e); } } else { completeUserLogin(request, response, code, clientID, clientSecret, holder, ac.getAuthInfo()); } } } private void processUserInfoRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, JoseException, InvalidJwtException, UnsupportedEncodingException { AuthController ac = (AuthController) request.getSession().getAttribute(ProxyConstants.AUTH_CTL); UrlHolder holder = (UrlHolder) request.getAttribute(ProxyConstants.AUTOIDM_CFG); holder.getApp().getCookieConfig().getTimeout(); String header = request.getHeader("Authorization"); if (header == null) { AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, ac.getAuthInfo(), "NONE"); response.sendError(401); return; } String accessToken = header.substring("Bearer ".length()); OIDCSession dbSession = this.getSessionByAccessToken(accessToken); if (dbSession == null) { AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, ac.getAuthInfo(), "NONE"); response.sendError(401); return; } JsonWebSignature jws = new JsonWebSignature(); jws.setCompactSerialization(dbSession.getIdToken()); jws.setKey(GlobalEntries.getGlobalEntries().getConfigManager().getCertificate(this.jwtSigningKeyName) .getPublicKey()); if (!jws.verifySignature()) { logger.warn("id_token tampered with"); AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, ac.getAuthInfo(), "NONE"); response.sendError(401); return; } JwtClaims claims = JwtClaims.parse(jws.getPayload()); claims.setGeneratedJwtId(); // a unique identifier for the token claims.setIssuedAtToNow(); // when the token was issued/created (now) claims.setNotBeforeMinutesInThePast( trusts.get(dbSession.getClientID()).getAccessTokenSkewMillis() / 1000 / 60); // time before which the token is not yet valid (2 minutes ago) claims.setExpirationTimeMinutesInTheFuture( trusts.get(dbSession.getClientID()).getAccessTokenTimeToLive() / 1000 / 60); // time when the token will expire (10 minutes from now) jws = new JsonWebSignature(); jws.setPayload(claims.toJson()); jws.setKey(GlobalEntries.getGlobalEntries().getConfigManager().getPrivateKey(this.jwtSigningKeyName)); jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); response.setContentType("application/jwt"); response.getOutputStream().write(jws.getCompactSerialization().getBytes("UTF-8")); AuthInfo remUser = new AuthInfo(); remUser.setUserDN(dbSession.getUserDN()); AccessLog.log(AccessEvent.AzSuccess, holder.getApp(), (HttpServletRequest) request, remUser, "NONE"); } private void refreshToken(HttpServletResponse response, String clientID, String clientSecret, String refreshToken, UrlHolder holder, HttpServletRequest request, AuthInfo authData) throws Exception, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException, JoseException, InvalidJwtException, UnsupportedEncodingException { Gson gson = new Gson(); String json = this.inflate(refreshToken); Token token = gson.fromJson(json, Token.class); byte[] iv = org.bouncycastle.util.encoders.Base64.decode(token.getIv()); IvParameterSpec spec = new IvParameterSpec(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, GlobalEntries.getGlobalEntries().getConfigManager() .getSecretKey(this.trusts.get(clientID).getCodeLastmileKeyName()), spec); byte[] encBytes = org.bouncycastle.util.encoders.Base64.decode(token.getEncryptedRequest()); String decryptedRefreshToken = new String(cipher.doFinal(encBytes)); OIDCSession session = this.getSessionByRefreshToken(decryptedRefreshToken); if (session == null) { logger.warn("Session does not exist from refresh_token"); AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, authData, "NONE"); response.sendError(401); return; } OpenIDConnectTrust trust = this.trusts.get(session.getClientID()); if (!trust.isPublicEndpoint()) { if (!trust.getClientSecret().equals(clientSecret)) { logger.warn("Invalid client_secret"); AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, authData, "NONE"); response.sendError(401); return; } } JsonWebSignature jws = new JsonWebSignature(); jws.setCompactSerialization(session.getIdToken()); jws.setKey(GlobalEntries.getGlobalEntries().getConfigManager().getCertificate(this.jwtSigningKeyName) .getPublicKey()); if (!jws.verifySignature()) { logger.warn("id_token tampered with"); AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, authData, "NONE"); response.sendError(401); return; } JwtClaims claims = JwtClaims.parse(jws.getPayload()); claims.setGeneratedJwtId(); // a unique identifier for the token claims.setIssuedAtToNow(); // when the token was issued/created (now) claims.setNotBeforeMinutesInThePast(trusts.get(clientID).getAccessTokenSkewMillis() / 1000 / 60); // time before which the token is not yet valid (2 minutes ago) claims.setExpirationTimeMinutesInTheFuture(trusts.get(clientID).getAccessTokenTimeToLive() / 1000 / 60); // time when the token will expire (10 minutes from now) jws = new JsonWebSignature(); jws.setPayload(claims.toJson()); jws.setKey(GlobalEntries.getGlobalEntries().getConfigManager().getPrivateKey(this.jwtSigningKeyName)); jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); session.setIdToken(jws.getCompactSerialization()); jws = new JsonWebSignature(); jws.setKey(GlobalEntries.getGlobalEntries().getConfigManager().getCertificate(this.jwtSigningKeyName) .getPublicKey()); jws.setCompactSerialization(session.getAccessToken()); if (!jws.verifySignature()) { logger.warn("access_token tampered with"); AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, authData, "NONE"); response.sendError(401); return; } claims = JwtClaims.parse(jws.getPayload()); claims.setGeneratedJwtId(); // a unique identifier for the token claims.setIssuedAtToNow(); // when the token was issued/created (now) claims.setNotBeforeMinutesInThePast(trusts.get(clientID).getAccessTokenSkewMillis() / 1000 / 60); // time before which the token is not yet valid (2 minutes ago) claims.setExpirationTimeMinutesInTheFuture(trusts.get(clientID).getAccessTokenTimeToLive() / 1000 / 60); // time when the token will expire (10 minutes from now) jws = new JsonWebSignature(); jws.setPayload(claims.toJson()); jws.setKey(GlobalEntries.getGlobalEntries().getConfigManager().getPrivateKey(this.jwtSigningKeyName)); jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); jws.setKeyIdHeaderValue(this.buildKID( GlobalEntries.getGlobalEntries().getConfigManager().getCertificate(this.jwtSigningKeyName))); session.setAccessToken(jws.getCompactSerialization()); UUID newRefreshToken = UUID.randomUUID(); session.setRefreshToken(newRefreshToken.toString()); String b64 = encryptToken(trusts.get(clientID).getCodeLastmileKeyName(), gson, newRefreshToken); session.setEncryptedRefreshToken(b64); Session db = null; try { db = this.sessionFactory.openSession(); OIDCSession loadSession = db.get(OIDCSession.class, session.getId()); loadSession.setIdToken(session.getIdToken()); loadSession.setAccessToken(session.getAccessToken()); loadSession.setRefreshToken(session.getRefreshToken()); loadSession.setEncryptedRefreshToken(session.getEncryptedRefreshToken()); loadSession.setClientID(session.getClientID()); loadSession.setUserDN(session.getUserDN()); db.beginTransaction(); db.save(loadSession); db.getTransaction().commit(); } finally { if (db != null) { if (db.getTransaction() != null && db.getTransaction().isActive()) { db.getTransaction().rollback(); } db.close(); } } OpenIDConnectAccessToken access = new OpenIDConnectAccessToken(); access.setAccess_token(session.getAccessToken()); access.setExpires_in((int) (trusts.get(clientID).getAccessTokenTimeToLive() / 1000)); access.setId_token(session.getIdToken()); access.setToken_type("Bearer"); access.setRefresh_token(session.getEncryptedRefreshToken()); json = gson.toJson(access); response.setContentType("text/json"); response.getOutputStream().write(json.getBytes()); response.getOutputStream().flush(); AuthInfo remUser = new AuthInfo(); remUser.setUserDN(session.getUserDN()); AccessLog.log(AccessEvent.AzSuccess, holder.getApp(), (HttpServletRequest) request, remUser, "NONE"); } private void completeUserLogin(HttpServletRequest request, HttpServletResponse response, String code, String clientID, String clientSecret, UrlHolder holder, AuthInfo authData) throws ServletException, IOException, MalformedURLException { String lastMileToken = null; try { lastMileToken = this.inflate(code); lastMileToken = new String( org.bouncycastle.util.encoders.Base64.encode(lastMileToken.getBytes("UTF-8"))); } catch (Exception e) { throw new ServletException("Could not inflate code", e); } OpenIDConnectTrust trust = this.trusts.get(clientID); if (!trust.isPublicEndpoint()) { if (!clientSecret.equals(trust.getClientSecret())) { AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, authData, "NONE"); response.sendError(403); return; } } ConfigManager cfg = (ConfigManager) request.getAttribute(ProxyConstants.TREMOLO_CFG_OBJ); SecretKey codeKey = cfg.getSecretKey(trust.getCodeLastmileKeyName()); com.tremolosecurity.lastmile.LastMile lmreq = new com.tremolosecurity.lastmile.LastMile(); try { lmreq.loadLastMielToken(lastMileToken, codeKey); } catch (Exception e) { logger.warn("Could not decrypt code token", e); response.sendError(403); AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, authData, "NONE"); return; } if (!lmreq.isValid()) { response.sendError(403); logger.warn("Could not validate code token"); AccessLog.log(AccessEvent.AzFail, holder.getApp(), (HttpServletRequest) request, authData, "NONE"); return; } Attribute dn = null; Attribute scopes = null; String nonce = null; for (Attribute attr : lmreq.getAttributes()) { if (attr.getName().equalsIgnoreCase("dn")) { dn = attr; } else if (attr.getName().equalsIgnoreCase("scope")) { scopes = attr; } else if (attr.getName().equalsIgnoreCase("nonce")) { nonce = attr.getValues().get(0); } } ConfigManager cfgMgr = (ConfigManager) request.getAttribute(ProxyConstants.TREMOLO_CFG_OBJ); DateTime now = new DateTime(); DateTime notBefore = now.minus(trust.getCodeTokenTimeToLive()); DateTime notAfter = now.plus(trust.getCodeTokenTimeToLive()); int authLevel = lmreq.getLoginLevel(); String authMethod = lmreq.getAuthChain(); try { lmreq = new com.tremolosecurity.lastmile.LastMile(request.getRequestURI(), notBefore, notAfter, authLevel, authMethod); } catch (URISyntaxException e) { throw new ServletException("Could not request access token", e); } /* lmreq.getAttributes().add(new Attribute("dn",dn.getValues().get(0))); SecretKey key = cfgMgr.getSecretKey(trust.getAccessLastmileKeyName()); String accessToken = null; try { accessToken = lmreq.generateLastMileToken(key); } catch (Exception e) { throw new ServletException("Could not generate access token",e); }*/ String accessToken = null; try { accessToken = this .produceJWT(this.generateClaims(dn.getValues().get(0), cfgMgr, new URL(request.getRequestURL().toString()), trust, nonce), cfgMgr) .getCompactSerialization(); } catch (JoseException | LDAPException | ProvisioningException e1) { throw new ServletException("Could not generate jwt", e1); } OpenIDConnectAccessToken access = new OpenIDConnectAccessToken(); access.setAccess_token(accessToken); access.setExpires_in((int) (trust.getAccessTokenTimeToLive() / 1000)); try { access.setId_token(this .produceJWT(this.generateClaims(dn.getValues().get(0), cfgMgr, new URL(request.getRequestURL().toString()), trust, nonce), cfgMgr) .getCompactSerialization()); } catch (Exception e) { throw new ServletException("Could not generate JWT", e); } access.setToken_type("Bearer"); OIDCSession oidcSession = null; try { oidcSession = this.storeSession(access, holder.getApp(), trust.getCodeLastmileKeyName(), request, dn.getValues().get(0), clientID); } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) { throw new ServletException("Could not store session", e); } access.setRefresh_token(oidcSession.getEncryptedRefreshToken()); Gson gson = new Gson(); String json = gson.toJson(access); response.setContentType("text/json"); response.getOutputStream().write(json.getBytes("UTF-8")); response.getOutputStream().flush(); if (logger.isDebugEnabled()) { logger.debug("Token JSON : '" + json + "'"); } AuthInfo remUser = new AuthInfo(); remUser.setUserDN(dn.getValues().get(0)); AccessLog.log(AccessEvent.AzSuccess, holder.getApp(), (HttpServletRequest) request, remUser, "NONE"); } public OIDCSession storeSession(OpenIDConnectAccessToken access, ApplicationType app, String codeTokenKeyName, HttpServletRequest request, String userDN, String clientID) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, IOException { Gson gson = new Gson(); OIDCSession session = new OIDCSession(); session.setAccessToken(access.getAccess_token()); session.setIdToken(access.getId_token()); session.setApplicationName(app.getName()); session.setSessionExpires( new Timestamp(new DateTime().plusSeconds(app.getCookieConfig().getTimeout()).getMillis())); session.setUserDN(userDN); session.setClientID(clientID); UUID refreshToken = UUID.randomUUID(); session.setRefreshToken(refreshToken.toString()); String b64 = encryptToken(codeTokenKeyName, gson, refreshToken); session.setEncryptedRefreshToken(b64); Session db = null; try { db = this.sessionFactory.openSession(); db.beginTransaction(); db.save(session); db.getTransaction().commit(); LogoutUtil.insertFirstLogoutHandler(request, new ClearOidcSessionOnLogout(session, this)); return session; } finally { if (db != null) { if (db.getTransaction() != null && db.getTransaction().isActive()) { db.getTransaction().rollback(); } db.close(); } } } private String decryptToken(String codeTokenKeyName, Gson gson, String encrypted) throws Exception { String inflated = this.inflate(encrypted); Token token = gson.fromJson(inflated, Token.class); byte[] iv = org.bouncycastle.util.encoders.Base64.decode(token.getIv()); IvParameterSpec spec = new IvParameterSpec(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, GlobalEntries.getGlobalEntries().getConfigManager().getSecretKey(codeTokenKeyName), spec); byte[] decBytes = org.bouncycastle.util.encoders.Base64.decode(token.getEncryptedRequest()); return new String(cipher.doFinal(decBytes)); } private String encryptToken(String codeTokenKeyName, Gson gson, UUID refreshToken) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException { byte[] bjson = refreshToken.toString().getBytes("UTF-8"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, GlobalEntries.getGlobalEntries().getConfigManager().getSecretKey(codeTokenKeyName)); byte[] encJson = cipher.doFinal(bjson); String base64d = new String(org.bouncycastle.util.encoders.Base64.encode(encJson)); Token token = new Token(); token.setEncryptedRequest(base64d); token.setIv(new String(org.bouncycastle.util.encoders.Base64.encode(cipher.getIV()))); byte[] bxml = gson.toJson(token).getBytes("UTF-8"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DeflaterOutputStream compressor = new DeflaterOutputStream(baos, new Deflater(Deflater.BEST_COMPRESSION, true)); compressor.write(bxml); compressor.flush(); compressor.close(); String b64 = new String(org.bouncycastle.util.encoders.Base64.encode(baos.toByteArray())); return b64; } private String inflate(String saml) throws Exception { byte[] compressedData = org.bouncycastle.util.encoders.Base64.decode(saml); ByteArrayInputStream bin = new ByteArrayInputStream(compressedData); InflaterInputStream decompressor = new InflaterInputStream(bin, new Inflater(true)); //decompressor.setInput(compressedData); // Create an expandable byte array to hold the decompressed data ByteArrayOutputStream bos = new ByteArrayOutputStream(compressedData.length); // Decompress the data byte[] buf = new byte[1024]; int len; while ((len = decompressor.read(buf)) > 0) { bos.write(buf, 0, len); } try { bos.close(); } catch (IOException e) { } // Get the decompressed data byte[] decompressedData = bos.toByteArray(); String decoded = new String(decompressedData); return decoded; } public void doPut(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { } private void completeFederation(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, MalformedURLException { final OpenIDConnectTransaction transaction = (OpenIDConnectTransaction) request.getSession() .getAttribute(OpenIDConnectIdP.TRANSACTION_DATA); request.setAttribute(AzSys.FORCE, "true"); NextSys completeFed = new NextSys() { public void nextSys(final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException { //System.out.println("Authorized!!!!"); final AuthInfo authInfo = ((AuthController) request.getSession() .getAttribute(ProxyConstants.AUTH_CTL)).getAuthInfo(); UrlHolder holder = (UrlHolder) request.getAttribute(ProxyConstants.AUTOIDM_CFG); HttpFilterRequest filterReq = new HttpFilterRequestImpl(request, null); HttpFilterResponse filterResp = new HttpFilterResponseImpl(response); PostProcess postProc = new PostProcess() { @Override public void postProcess(HttpFilterRequest req, HttpFilterResponse resp, UrlHolder holder, HttpFilterChain chain) throws Exception { postResponse(transaction, request, response, authInfo, holder); } @Override public boolean addHeader(String name) { return false; } }; HttpFilterChain chain = new HttpFilterChainImpl(holder, postProc); try { chain.nextFilter(filterReq, filterResp, chain); } catch (Exception e) { throw new ServletException(e); } } }; AzSys az = new AzSys(); az.doAz(request, response, completeFed); } private void postResponse(OpenIDConnectTransaction transaction, HttpServletRequest request, HttpServletResponse response, AuthInfo authInfo, UrlHolder holder) throws Exception { //first generate a lastmile token OpenIDConnectTrust trust = trusts.get(transaction.getClientID()); ConfigManager cfgMgr = (ConfigManager) request.getAttribute(ProxyConstants.TREMOLO_CFG_OBJ); DateTime now = new DateTime(); DateTime notBefore = now.minus(trust.getCodeTokenTimeToLive()); DateTime notAfter = now.plus(trust.getCodeTokenTimeToLive()); com.tremolosecurity.lastmile.LastMile lmreq = new com.tremolosecurity.lastmile.LastMile( request.getRequestURI(), notBefore, notAfter, authInfo.getAuthLevel(), authInfo.getAuthMethod()); lmreq.getAttributes().add(new Attribute("dn", authInfo.getUserDN())); Attribute attr = new Attribute("scope"); attr.getValues().addAll(transaction.getScope()); lmreq.getAttributes().add(attr); if (transaction.getNonce() != null) { lmreq.getAttributes().add(new Attribute("nonce", transaction.getNonce())); } SecretKey key = cfgMgr.getSecretKey(trust.getCodeLastmileKeyName()); String codeToken = lmreq.generateLastMileToken(key); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DeflaterOutputStream compressor = new DeflaterOutputStream(baos, new Deflater(Deflater.BEST_COMPRESSION, true)); compressor.write(org.bouncycastle.util.encoders.Base64.decode(codeToken.getBytes("UTF-8"))); compressor.flush(); compressor.close(); String b64 = new String(org.bouncycastle.util.encoders.Base64.encode(baos.toByteArray())); StringBuffer b = new StringBuffer(); b.append(transaction.getRedirectURI()).append("?").append("code=").append(URLEncoder.encode(b64, "UTF-8")) .append("&state=").append(URLEncoder.encode(transaction.getState(), "UTF-8")); response.sendRedirect(b.toString()); } public void init(String idpName, ServletContext ctx, HashMap<String, Attribute> init, HashMap<String, HashMap<String, Attribute>> trustCfg, MapIdentity mapper) { final String localIdPName = idpName; this.idpName = idpName; this.trusts = new HashMap<String, OpenIDConnectTrust>(); for (String trustName : trustCfg.keySet()) { HashMap<String, Attribute> attrs = trustCfg.get(trustName); OpenIDConnectTrust trust = new OpenIDConnectTrust(); trust.setClientID(attrs.get("clientID").getValues().get(0)); trust.setClientSecret(attrs.get("clientSecret").getValues().get(0)); trust.setRedirectURI(attrs.get("redirectURI").getValues().get(0)); trust.setCodeLastmileKeyName(attrs.get("codeLastMileKeyName").getValues().get(0)); trust.setAuthChain(attrs.get("authChainName").getValues().get(0)); trust.setCodeTokenTimeToLive(Long.parseLong(attrs.get("codeTokenSkewMilis").getValues().get(0))); trust.setAccessTokenTimeToLive(Long.parseLong(attrs.get("accessTokenTimeToLive").getValues().get(0))); trust.setAccessTokenSkewMillis(Long.parseLong(attrs.get("accessTokenSkewMillis").getValues().get(0))); if (attrs.get("verifyRedirect") == null) { trust.setVerifyRedirect(true); } else { trust.setVerifyRedirect(attrs.get("verifyRedirect").getValues().get(0).equalsIgnoreCase("true")); } trust.setTrustName(trustName); if (attrs.get("publicEndpoint") != null && attrs.get("publicEndpoint").getValues().get(0).equalsIgnoreCase("true")) { trust.setPublicEndpoint(true); } trusts.put(trust.getClientID(), trust); } this.mapper = mapper; this.jwtSigningKeyName = init.get("jwtSigningKey").getValues().get(0); HashMap<String, OpenIDConnectIdP> oidcIdPs = (HashMap<String, OpenIDConnectIdP>) GlobalEntries .getGlobalEntries().get(UNISON_OPENIDCONNECT_IDPS); if (oidcIdPs == null) { oidcIdPs = new HashMap<String, OpenIDConnectIdP>(); GlobalEntries.getGlobalEntries().set(UNISON_OPENIDCONNECT_IDPS, oidcIdPs); } oidcIdPs.put(this.idpName, this); GlobalEntries.getGlobalEntries().getConfigManager().addThread(new StopableThread() { @Override public void run() { //do nothing } @Override public void stop() { HashMap<String, OpenIDConnectIdP> oidcIdPs = (HashMap<String, OpenIDConnectIdP>) GlobalEntries .getGlobalEntries().get(UNISON_OPENIDCONNECT_IDPS); if (oidcIdPs != null) { oidcIdPs.remove(localIdPName); } } }); String driver = init.get("driver").getValues().get(0); logger.info("Driver : '" + driver + "'"); String url = init.get("url").getValues().get(0); ; logger.info("URL : " + url); String user = init.get("user").getValues().get(0); ; logger.info("User : " + user); String pwd = init.get("password").getValues().get(0); ; logger.info("Password : **********"); int maxCons = Integer.parseInt(init.get("maxCons").getValues().get(0)); logger.info("Max Cons : " + maxCons); int maxIdleCons = Integer.parseInt(init.get("maxIdleCons").getValues().get(0)); logger.info("maxIdleCons : " + maxIdleCons); String dialect = init.get("dialect").getValues().get(0); logger.info("Hibernate Dialect : '" + dialect + "'"); String validationQuery = init.get("validationQuery").getValues().get(0); logger.info("Validation Query : '" + validationQuery + "'"); String hibernateConfig = init.get("hibernateConfig") != null ? init.get("hibernateConfig").getValues().get(0) : null; logger.info("HIbernate mapping file : '" + hibernateConfig + "'"); String hibernateCreateSchema = init.get("hibernateCreateSchema") != null ? init.get("hibernateCreateSchema").getValues().get(0) : null; logger.info("Can create schema : '" + hibernateCreateSchema + "'"); this.initializeHibernate(driver, user, pwd, url, dialect, maxCons, maxIdleCons, validationQuery, hibernateConfig, hibernateCreateSchema); } public JsonWebSignature generateJWS(JwtClaims claims) throws JoseException, LDAPException, ProvisioningException, MalformedURLException { return this.produceJWT(claims, GlobalEntries.getGlobalEntries().getConfigManager()); } public JwtClaims generateClaims(AuthInfo user, ConfigManager cfg, String trustName, String urlOfRequest) throws JoseException, LDAPException, ProvisioningException, MalformedURLException { String url = urlOfRequest; int end = url.indexOf('/', url.indexOf("://") + 3); if (end != -1) { url = url.substring(0, end); } return generateClaims(user.getUserDN(), cfg, new URL(url), this.trusts.get(trustName), null); } private JsonWebSignature produceJWT(JwtClaims claims, ConfigManager cfg) throws JoseException, LDAPException, ProvisioningException { //String dn,ConfigManager cfg,URL url,OpenIDConnectTrust trust,HttpServletRequest request,String nonce //JwtClaims claims = generateClaims(dn, cfg, url, trust, nonce); // A JWT is a JWS and/or a JWE with JSON claims as the payload. // In this example it is a JWS so we create a JsonWebSignature object. JsonWebSignature jws = new JsonWebSignature(); // The payload of the JWS is JSON content of the JWT Claims jws.setPayload(claims.toJson()); // The JWT is signed using the private key jws.setKey(cfg.getPrivateKey(this.jwtSigningKeyName)); // Set the Key ID (kid) header because it's just the polite thing to do. // We only have one key in this example but a using a Key ID helps // facilitate a smooth key rollover process //jws.setKeyIdHeaderValue(javax.xml.bind.DatatypeConverter.printHexBinary(cfg.getCertificate(jwtSigningKeyName).getExtensionValue("2.5.29.14"))); jws.setKeyIdHeaderValue(this.buildKID(cfg.getCertificate(this.jwtSigningKeyName))); // Set the signature algorithm on the JWT/JWS that will integrity protect the claims jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); return jws; } private JwtClaims generateClaims(String dn, ConfigManager cfg, URL url, OpenIDConnectTrust trust, String nonce) throws LDAPException, ProvisioningException { StringBuffer issuer = new StringBuffer(); issuer.append(url.getProtocol()).append("://").append(url.getHost()); if (url.getPort() > 0) { issuer.append(':').append(url.getPort()); } issuer.append(cfg.getAuthIdPPath()).append(this.idpName); // Create the Claims, which will be the content of the JWT JwtClaims claims = new JwtClaims(); claims.setIssuer(issuer.toString()); // who creates the token and signs it claims.setAudience(trust.getClientID()); // to whom the token is intended to be sent claims.setExpirationTimeMinutesInTheFuture(trust.getAccessTokenTimeToLive() / 1000 / 60); // time when the token will expire (10 minutes from now) claims.setGeneratedJwtId(); // a unique identifier for the token claims.setIssuedAtToNow(); // when the token was issued/created (now) claims.setNotBeforeMinutesInThePast(trust.getAccessTokenSkewMillis() / 1000 / 60); // time before which the token is not yet valid (2 minutes ago) //claims.setSubject(dn); // the subject/principal is whom the token is about if (nonce != null) { claims.setClaim("nonce", nonce); } ArrayList<String> attrs = new ArrayList<String>(); LDAPSearchResults res = cfg.getMyVD().search(dn, 0, "(objectClass=*)", attrs); res.hasMore(); LDAPEntry entry = res.next(); User user = new User(entry); user = this.mapper.mapUser(user, true); for (String attrName : user.getAttribs().keySet()) { Attribute attr = user.getAttribs().get(attrName); if (attr != null) { if (attr.getName().equalsIgnoreCase("sub")) { claims.setSubject(attr.getValues().get(0)); } else if (attr.getValues().size() == 1) { claims.setClaim(attrName, attr.getValues().get(0)); } else { claims.setStringListClaim(attrName, attr.getValues()); } } } return claims; } private void initializeHibernate(String driver, String user, String password, String url, String dialect, int maxCons, int maxIdleCons, String validationQuery, String mappingFile, String createSchema) { StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder(); Configuration config = new Configuration(); config.setProperty("hibernate.connection.driver_class", driver); config.setProperty("hibernate.connection.password", password); config.setProperty("hibernate.connection.url", url); config.setProperty("hibernate.connection.username", user); config.setProperty("hibernate.dialect", dialect); if (createSchema == null || createSchema.equalsIgnoreCase("true")) { config.setProperty("hibernate.hbm2ddl.auto", "update"); } config.setProperty("show_sql", "true"); config.setProperty("hibernate.current_session_context_class", "thread"); config.setProperty("hibernate.c3p0.max_size", Integer.toString(maxCons)); config.setProperty("hibernate.c3p0.maxIdleTimeExcessConnections", Integer.toString(maxIdleCons)); if (validationQuery != null && !validationQuery.isEmpty()) { config.setProperty("hibernate.c3p0.testConnectionOnCheckout", "true"); } config.setProperty("hibernate.c3p0.autoCommitOnClose", "true"); //config.setProperty("hibernate.c3p0.debugUnreturnedConnectionStackTraces", "true"); //config.setProperty("hibernate.c3p0.unreturnedConnectionTimeout", "30"); if (validationQuery == null) { validationQuery = "SELECT 1"; } config.setProperty("hibernate.c3p0.preferredTestQuery", validationQuery); LoadedConfig lc = null; if (mappingFile == null || mappingFile.trim().isEmpty()) { JaxbCfgHibernateConfiguration jaxbCfg = new JaxbCfgHibernateConfiguration(); jaxbCfg.setSessionFactory(new JaxbCfgSessionFactory()); JaxbCfgMappingReferenceType mrt = new JaxbCfgMappingReferenceType(); mrt.setClazz(OIDCSession.class.getName()); jaxbCfg.getSessionFactory().getMapping().add(mrt); lc = LoadedConfig.consume(jaxbCfg); } else { lc = LoadedConfig.baseline(); } StandardServiceRegistry registry = builder.configure(lc).applySettings(config.getProperties()).build(); try { if (mappingFile == null || mappingFile.trim().isEmpty()) { sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory(); } else { sessionFactory = new MetadataSources(registry).addResource(mappingFile).buildMetadata() .buildSessionFactory(); } GlobalEntries.getGlobalEntries().getConfigManager().addThread(new StopableThread() { @Override public void run() { } @Override public void stop() { logger.info("Stopping hibernate"); sessionFactory.close(); } }); } catch (Exception e) { e.printStackTrace(); // The registry would be destroyed by the SessionFactory, but we had trouble building the SessionFactory // so destroy it manually. StandardServiceRegistryBuilder.destroy(registry); } } public void removeSession(OIDCSession session) { Session db = null; try { db = this.sessionFactory.openSession(); //check to see if the object still exists OIDCSession lsession = db.get(OIDCSession.class, session.getId()); if (lsession != null) { db.beginTransaction(); db.delete(lsession); db.getTransaction().commit(); } } finally { if (db != null) { if (db.getTransaction() != null && db.getTransaction().isActive()) { db.getTransaction().rollback(); } db.close(); } } } public HashMap<String, OpenIDConnectTrust> getTrusts() { return trusts; } public OIDCSession getSessionByRefreshToken(String refreshToken) { Session db = null; try { db = this.sessionFactory.openSession(); String hql = "FROM OIDCSession o WHERE o.refreshToken = :refresh_token"; Query query = db.createQuery(hql); query.setParameter("refresh_token", refreshToken); List<OIDCSession> results = query.list(); if (results == null || results.isEmpty()) { return null; } OIDCSession session = results.get(0); if (new DateTime(session.getSessionExpires()).isBeforeNow()) { db.beginTransaction(); db.delete(session); db.getTransaction().commit(); return null; } else { return session; } } finally { if (db != null) { if (db.getTransaction() != null && db.getTransaction().isActive()) { db.getTransaction().rollback(); } db.close(); } } } public OIDCSession getSessionByAccessToken(String accessToken) { Session db = null; try { db = this.sessionFactory.openSession(); String hql = "FROM OIDCSession o WHERE o.accessToken = :access_token"; Query query = db.createQuery(hql); query.setParameter("access_token", accessToken); List<OIDCSession> results = query.list(); if (results == null || results.isEmpty()) { return null; } OIDCSession session = results.get(0); if (new DateTime(session.getSessionExpires()).isBeforeNow()) { db.beginTransaction(); db.delete(session); db.getTransaction().commit(); return null; } else { return session; } } finally { if (db != null) { if (db.getTransaction() != null && db.getTransaction().isActive()) { db.getTransaction().rollback(); } db.close(); } } } public void updateToken(OIDCSession session) { Session db = null; ApplicationType app = null; for (ApplicationType at : GlobalEntries.getGlobalEntries().getConfigManager().getCfg().getApplications() .getApplication()) { if (at.getName().equals(session.getApplicationName())) { app = at; } } try { db = this.sessionFactory.openSession(); //check to see if the object still exists OIDCSession lsession = db.get(OIDCSession.class, session.getId()); if (lsession != null) { db.beginTransaction(); lsession.setAccessToken(session.getAccessToken()); lsession.setIdToken(session.getIdToken()); lsession.setSessionExpires( new Timestamp(System.currentTimeMillis() + (app.getCookieConfig().getTimeout() * 1000))); db.save(lsession); db.getTransaction().commit(); } } finally { if (db != null) { if (db.getTransaction() != null && db.getTransaction().isActive()) { db.getTransaction().rollback(); } db.close(); } } } public String decryptClientSecret(String keyName, String encryptedClientSecret) throws Exception { return this.decryptToken(keyName, new Gson(), encryptedClientSecret); } public OIDCSession reloadSession(OIDCSession oidcSession) { Session db = null; try { db = this.sessionFactory.openSession(); //check to see if the object still exists OIDCSession lsession = db.get(OIDCSession.class, oidcSession.getId()); return lsession; } finally { if (db != null) { if (db.getTransaction() != null && db.getTransaction().isActive()) { db.getTransaction().rollback(); } db.close(); } } } public String getJwtSigningKeyName() { return this.jwtSigningKeyName; } public void clearExpiredSessions() { Session db = null; try { db = this.sessionFactory.openSession(); db.beginTransaction(); String hql = "DELETE FROM OIDCSession o WHERE o.sessionExpires <= :exp_ts"; Query query = db.createQuery(hql); query.setParameter("exp_ts", new Timestamp(System.currentTimeMillis())); query.executeUpdate(); db.getTransaction().commit(); } finally { if (db != null) { if (db.getTransaction() != null && db.getTransaction().isActive()) { db.getTransaction().rollback(); } db.close(); } } } }