/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.security.sso.openid.connect.internal; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.security.sso.openid.connect.OpenIdConnectProviderMetadataFactory; import com.liferay.portal.security.sso.openid.connect.OpenIdConnectServiceException; import com.nimbusds.oauth2.sdk.ParseException; import com.nimbusds.oauth2.sdk.http.HTTPRequest; import com.nimbusds.oauth2.sdk.http.HTTPResponse; import com.nimbusds.oauth2.sdk.id.Issuer; import com.nimbusds.openid.connect.sdk.SubjectType; import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.List; import net.minidev.json.JSONObject; import org.apache.commons.lang3.time.StopWatch; /** * @author Edward C. Han */ public class OpenIdConnectProviderMetadataFactoryImpl implements OpenIdConnectProviderMetadataFactory { public OpenIdConnectProviderMetadataFactoryImpl( String providerName, String issuerURL, String[] subjectTypes, String jwksURL, String authorizationEndPointURL, String tokenEndPointURL, String userInfoEndPointURL) throws OpenIdConnectServiceException.ProviderException { _providerName = providerName; _cacheInMilliseconds = 0; _discoveryEndPointURL = null; try { List<SubjectType> subjectTypesList = new ArrayList<>(); for (String subjectType : subjectTypes) { subjectTypesList.add(SubjectType.parse(subjectType)); } _oidcProviderMetadata = new OIDCProviderMetadata( new Issuer(issuerURL), subjectTypesList, new URI(jwksURL)); _oidcProviderMetadata.setAuthorizationEndpointURI( new URI(authorizationEndPointURL)); _oidcProviderMetadata.setTokenEndpointURI( new URI(tokenEndPointURL)); _oidcProviderMetadata.setUserInfoEndpointURI( new URI(userInfoEndPointURL)); } catch (ParseException pe) { throw new OpenIdConnectServiceException.ProviderException( "Invalid subject types for OpenId Connect provider " + _providerName, pe); } catch (URISyntaxException urise) { throw new OpenIdConnectServiceException.ProviderException( "Invalid URLs for OpenId Connect provider " + _providerName, urise); } } public OpenIdConnectProviderMetadataFactoryImpl( String providerName, URL discoveryEndPointURL) { this(providerName, discoveryEndPointURL, 0); } public OpenIdConnectProviderMetadataFactoryImpl( String providerName, URL discoveryEndPointURL, long cacheInMilliseconds) { _providerName = providerName; _discoveryEndPointURL = discoveryEndPointURL; _cacheInMilliseconds = cacheInMilliseconds; } @Override public OIDCProviderMetadata getOIDCProviderMetadata() throws OpenIdConnectServiceException.ProviderException { long currentTime = System.currentTimeMillis(); if (needsRefresh(currentTime)) { refresh(currentTime); } return _oidcProviderMetadata; } protected boolean needsRefresh(long time) { if (_oidcProviderMetadata == null) { if (_log.isInfoEnabled()) { _log.info( "Refreshing new OpenId Connect provider " + _providerName); } return true; } long elapsedTime = time - _lastRefreshTimestamp; if ((_cacheInMilliseconds > 0) && (elapsedTime > _cacheInMilliseconds)) { if (_log.isInfoEnabled()) { _log.info( "Refreshing stale OpenId Connect provider " + _providerName); } return true; } return false; } protected synchronized void refresh(long time) throws OpenIdConnectServiceException.ProviderException { if (needsRefresh(time)) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); try { HTTPRequest httpRequest = new HTTPRequest( HTTPRequest.Method.GET, _discoveryEndPointURL); HTTPResponse httpResponse = httpRequest.send(); JSONObject jsonObject = httpResponse.getContentAsJSONObject(); _oidcProviderMetadata = OIDCProviderMetadata.parse(jsonObject); _lastRefreshTimestamp = time; } catch (IOException | ParseException e) { throw new OpenIdConnectServiceException.ProviderException( "Unable to get OpenId Connect provider metadata for " + _providerName, e); } finally { stopWatch.stop(); if (_log.isInfoEnabled()) { _log.info( "Getting OpenId Connect provider metadata from " + _discoveryEndPointURL + " took " + stopWatch.getTime() + "ms"); } } } } private static final Log _log = LogFactoryUtil.getLog( OpenIdConnectProviderMetadataFactoryImpl.class); private final long _cacheInMilliseconds; private final URL _discoveryEndPointURL; private long _lastRefreshTimestamp; private OIDCProviderMetadata _oidcProviderMetadata; private final String _providerName; }