OAuth2PrincipalReader.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2025 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.utils.oauth2;
import java.util.Properties;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.transport.http.credential.IPrincipalReader;
import org.openspcoop2.utils.transport.http.credential.PrincipalReaderException;
import org.slf4j.Logger;
/**
* Implementazione dell'interfaccia {@link IPrincipalReader} che legge il principal dalla sessione, dopo che l'utenza e' stata autenticata con oauth2.
* Durante la fase di lettura del principal dalla sessione viene effettuato anche il controllo di validata del token e se previsto viene fatto il refresh.
*
* @author Pintori Giuliano (pintori@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class OAuth2PrincipalReader implements IPrincipalReader {
private transient Logger log = null;
private Properties properties = null;
/**
*
*/
private static final long serialVersionUID = 1L;
public OAuth2PrincipalReader(Logger log){
this.log = log;
this.log.debug("OAuth2PrincipalReader inizializzato.");
}
@Override
public String getPrincipal(HttpServletRequest request) throws PrincipalReaderException {
this.log.debug("Estrazione principal in corso...");
HttpSession session = request.getSession(false);
String principalClaim = this.properties.getProperty(OAuth2Costanti.PROP_OAUTH2_PRINCIPAL_CLAIM);
// Se l’utente NON è autenticato e non siamo già in login
boolean loggedIn = (session != null && session.getAttribute(OAuth2Costanti.ATTRIBUTE_NAME_USER_INFO) != null);
if(loggedIn) {
Oauth2UserInfo userInfo = (Oauth2UserInfo) session.getAttribute(OAuth2Costanti.ATTRIBUTE_NAME_USER_INFO);
String principal = (String) userInfo.getMap().get(principalClaim);
if (principal == null) {
this.log.warn("Principal claim [{}] non trovato in userInfo: {}", principalClaim, userInfo.getMap());
return null;
}
Long expiresAt = (Long) session.getAttribute(OAuth2Costanti.ATTRIBUTE_NAME_TOKEN_EXPIRES_AT);
String refreshToken = (String) session.getAttribute(OAuth2Costanti.ATTRIBUTE_NAME_REFRESH_TOKEN);
// token non scade mai o non ancora scaduto
if (expiresAt == null || expiresAt < DateManager.getTimeMillis()) {
this.log.debug("Username collegato: [{}]", principal);
return principal;
}
// token scaduto, ma non ho un refresh token
if (refreshToken == null) {
this.log.warn("Token scaduto e non è disponibile un refresh token. L'utente deve effettuare nuovamente il login.");
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_CODE, OAuth2Costanti.ERRORE_ACCESS_DENIED);
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_DETAIL, "Token scaduto e non disponibile un refresh token.");
return null;
}
OAuth2Token oAuth2Token = refreshToken(request, refreshToken);
// richiesta dei certificati
Oauth2BaseResponse jwksResponse = OAuth2Utilities.getCertificati(this.log, this.properties);
// Verifica errori nella risposta
if (jwksResponse.getReturnCode() != 200) {
// Errore nel richiedere i certificati
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_CODE, jwksResponse.getError());
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_DETAIL, jwksResponse.getDescription());
return null;
}
// validazione token (firma + claim configurati)
boolean valida = OAuth2Utilities.isValidToken(this.log, this.properties, jwksResponse, oAuth2Token);
if (!valida) {
// Token ricevuto non valido
logError(this.log, OAuth2Costanti.ERROR_MSG_TOKEN_RICEVUTO_NON_VALIDO);
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_CODE, OAuth2Costanti.ERROR_MSG_TOKEN_RICEVUTO_NON_VALIDO);
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_DETAIL, OAuth2Costanti.ERROR_MSG_TOKEN_RICEVUTO_NON_VALIDO);
return null;
}
Oauth2UserInfo oauth2UserInfo = OAuth2Utilities.getUserInfo(this.log, this.properties, oAuth2Token);
// Verifica errori nella risposta
if (oauth2UserInfo.getReturnCode() != 200) {
// Errore nel richiedere userinfo
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_CODE, oauth2UserInfo.getError());
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_DETAIL, oauth2UserInfo.getDescription());
return null;
}
// Salva token e scadenza se vuoi gestire refresh
session.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ACCESS_TOKEN, oAuth2Token.getAccessToken());
session.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ID_TOKEN, oAuth2Token.getIdToken());
session.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ACCESS_TOKEN_OBJ, oAuth2Token);
session.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_REFRESH_TOKEN, oAuth2Token.getRefreshToken());
session.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_TOKEN_EXPIRES_AT, oAuth2Token.getExpiresAt());
session.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_USER_INFO, oauth2UserInfo);
this.log.debug("Username collegato: [{}]", principal);
return principal;
}
this.log.warn("Utenza non autenticata");
return null;
}
private OAuth2Token refreshToken(HttpServletRequest request, String refreshToken) {
OAuth2Token oAuth2Token = OAuth2Utilities.refreshToken(this.log, this.properties, refreshToken);
// Verifica errori nella risposta
if (oAuth2Token.getReturnCode() != 200) {
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_CODE, oAuth2Token.getError());
request.setAttribute(OAuth2Costanti.ATTRIBUTE_NAME_ERROR_DETAIL, oAuth2Token.getDescription());
}
return oAuth2Token;
}
@Override
public void init(Object... parametri) throws PrincipalReaderException {
if(parametri!=null && parametri.length>0) {
Object pObject = parametri[0];
if(pObject instanceof Properties) {
this.properties = (Properties) pObject;
}
}
}
private static void logError(Logger log, String msg) {
logError(log, msg, null);
}
private static void logError(Logger log, String msg, Throwable e) {
if(e != null) {
log.error(msg,e);
} else {
log.error(msg);
}
}
}