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