001/*- 002 * #%L 003 * Smile CDR - CDR 004 * %% 005 * Copyright (C) 2016 - 2025 Smile CDR, Inc. 006 * %% 007 * All rights reserved. 008 * #L% 009 */ 010package ca.cdr.api.security; 011 012import ca.cdr.api.annotations.CdrPublicAPI; 013import ca.cdr.api.log.Logs; 014import ca.cdr.api.model.json.OpenIdTokenResponseJson; 015import ca.cdr.api.model.json.oauth.OpenIdWellKnownOpenIdConfigurationResponseJson; 016import jakarta.annotation.Nonnull; 017import org.apache.commons.lang3.StringUtils; 018import org.apache.http.client.utils.URIBuilder; 019import org.apache.http.message.BasicNameValuePair; 020import org.slf4j.Logger; 021 022import java.net.URISyntaxException; 023import java.time.Instant; 024import java.util.List; 025 026/** 027 * Keeps authentication state properties. After initialization only one of myJwt or mySecret properties 028 * must be valid depending on the authorization flow to be used: 029 * <ul><li>myJwt for private_key_jwt workflow</li> 030 * <li>mySecret for client_secret workflow</li></ul> 031 */ 032@CdrPublicAPI 033public class ClientAuthState { 034 private static final Logger ourLog = Logs.getSecurityTroubleshootingLog(); 035 036 // safe margin to make sure that token doesn't expire while the request is sent 037 static final long SAFE_EXPIRATION_MARGIN_SECS = 2; 038 039 private final ClientAuthParams myParams; 040 041 /** 042 * Url of the issuer service which will authenticate the access token 043 */ 044 private String myIssuerServiceBase; 045 046 /** 047 * Access token, once obtained 048 */ 049 private OpenIdTokenResponseJson myAccessToken; 050 051 /** Access token expiration */ 052 private long myTokenExpiresInSeconds; 053 054 private OpenIdWellKnownOpenIdConfigurationResponseJson myWellKnownResponse; 055 056 public ClientAuthState(@Nonnull ClientAuthParams theParameters) { 057 if (!StringUtils.isBlank(theParameters.getBaseUrl())) { 058 myIssuerServiceBase = theParameters.getBaseUrl(); 059 } 060 myParams = theParameters; 061 } 062 063 public boolean isAccessTokenExpired() { 064 long secondsNow = Instant.now().getEpochSecond(); 065 066 ourLog.atDebug() 067 .setMessage( 068 """ 069 070 myTokenExpiresAt : {} 071 Now : {} 072 SAFE_EXPIRATION_MARGIN_SECS : {} 073 secondsNow + SAFE_EXPIRATION_MARGIN_SECS: {} 074 token is {}expired""") 075 .addArgument(myTokenExpiresInSeconds) 076 .addArgument(secondsNow) 077 .addArgument(String.format("%10s", SAFE_EXPIRATION_MARGIN_SECS)) 078 .addArgument(secondsNow + SAFE_EXPIRATION_MARGIN_SECS) 079 .addArgument(myTokenExpiresInSeconds < secondsNow + SAFE_EXPIRATION_MARGIN_SECS ? "" : "not ") 080 .log(); 081 082 return myTokenExpiresInSeconds < secondsNow + SAFE_EXPIRATION_MARGIN_SECS; 083 } 084 085 public boolean isForceHttpInTokenRequestAudience() { 086 return myParams.isForceHttpInTokenRequestAudience(); 087 } 088 089 public String getKeystoreName() { 090 return myParams.getKeystoreName(); 091 } 092 093 public OpenIdTokenResponseJson getAccessToken() { 094 return myAccessToken; 095 } 096 097 public void setAccessToken(OpenIdTokenResponseJson theToken) { 098 myAccessToken = theToken; 099 } 100 101 public String getTokenEndpoint() { 102 return myWellKnownResponse == null ? null : myWellKnownResponse.getTokenEndpoint(); 103 } 104 105 public String getIssuerServiceBase() { 106 return myIssuerServiceBase; 107 } 108 109 public void setIssuerServiceBase(@Nonnull String theUrl) { 110 String baseUrl = myParams.getBaseUrl() != null ? myParams.getBaseUrl() : theUrl; 111 try { 112 URIBuilder builder = new URIBuilder(baseUrl); 113 myIssuerServiceBase = builder.setPath("").build().toString(); 114 } catch (URISyntaxException e) { 115 throw new IllegalArgumentException("Invalid URL: " + baseUrl, e); 116 } 117 } 118 119 public String getClientId() { 120 return myParams.getClientId(); 121 } 122 123 public void setTokenExpiresInSeconds(long theExpSecs) { 124 myTokenExpiresInSeconds = theExpSecs; 125 } 126 127 public List<BasicNameValuePair> getCustomTokenRequestParams() { 128 return myParams.getCustomTokenRequestParams(); 129 } 130 131 public String getScope() { 132 return myParams.getScope(); 133 } 134 135 public void setWellKnownConfig(OpenIdWellKnownOpenIdConfigurationResponseJson theWellKnownResponse) { 136 myWellKnownResponse = theWellKnownResponse; 137 } 138 139 public String getClientSecret() { 140 return myParams.getClientSecret(); 141 } 142 143 // casting to Object to avoid publishing ITlsComponentConfig 144 public Object getClientTlsConfigObject() { 145 return myParams.getClientTlsConfig(); 146 } 147}