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.OpenIdTokenResponse; 015import ca.cdr.api.model.json.oauth.OpenIdWellKnownOpenIdConfigurationResponse; 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 OpenIdTokenResponse myAccessToken; 049 050 /** Access token expiration */ 051 private long myTokenExpiresInSeconds; 052 053 private OpenIdWellKnownOpenIdConfigurationResponse 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 OpenIdTokenResponse getAccessToken() { 090 return myAccessToken; 091 } 092 093 public void setAccessToken(OpenIdTokenResponse 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 url) { 106 try { 107 URIBuilder builder = new URIBuilder(url); 108 myTargetServiceBase = builder.setPath("").build().toString(); 109 } catch (URISyntaxException e) { 110 throw new IllegalArgumentException("Invalid URL: " + url, e); 111 } 112 } 113 114 public String getClientId() { 115 return myParams.getClientId(); 116 } 117 118 public void setTokenExpiresInSeconds(long theExpSecs) { 119 myTokenExpiresInSeconds = theExpSecs; 120 } 121 122 public List<BasicNameValuePair> getCustomTokenRequestParams() { 123 return myParams.getCustomTokenRequestParams(); 124 } 125 126 public String getScope() { 127 return myParams.getScope(); 128 } 129 130 public void setWellKnownConfig(OpenIdWellKnownOpenIdConfigurationResponse theWellKnownResponse) { 131 myWellKnownResponse = theWellKnownResponse; 132 } 133 134 public String getClientSecret() { 135 return myParams.getClientSecret(); 136 } 137 138 // casting to Object to avoid publishing ITlsComponentConfig 139 public Object getClientTlsConfigObject() { 140 return myParams.getClientTlsConfig(); 141 } 142}