Overview
The authentication system provides secure login, automatic token refresh, and session management for HL7 connectivity operations. All HL7 operations require an active authenticated session.
Authentication Flow
Login Process
The AuthService.login() method establishes an authenticated session:
public void login (
String email,
char [] password,
String apiKey,
Environment environment
)
User email address for authentication
User password (char array for security)
API key provided by the HL7 provider
Target environment (DEV, QA, PRE, or PRD)
Device Request Creation
During login, the client automatically creates a DeviceRequest with unique device identification:
private DeviceRequest createDevice () {
String deviceId = UUID . randomUUID (). toString ();
return new DeviceRequest (deviceId, deviceId, "HL7-Java-Client" );
}
DeviceRequest Structure:
Unique messaging identifier (UUID)
Unique device identifier (UUID)
Device name (default: “HL7-Java-Client”)
Device blocked status (always false)
Remember device flag (always false)
Login Request Structure
The complete login request sent to the authentication endpoint:
LoginRequest request = new LoginRequest (
apiKey,
email,
new String (password),
device
);
Login Response
Successful authentication returns a LoginResponse:
LoginResponse loginResponse = JsonUtil . fromJson ( response . getBody (), LoginResponse . class );
JWT access token for authenticated requests
Token expiration timestamp (format: yyyyMMddHHmmss)
Provider-specific data including CUIT and matriculation details
Session Management
SessionContext Initialization
After successful login, the session is initialized with all authentication data:
private void initializeSession (
LoginResponse response,
Environment environment,
DeviceRequest device
) {
SessionContext . initialize (
response . getToken (),
response . getExp (),
response . getModelEspecifico (),
environment,
device
);
SessionRefreshManager . ensureStarted ( this );
}
The SessionContext stores:
Authentication token
Token expiration timestamp
Provider (Prestador) information
Environment configuration
Device information
SessionContext uses thread-safe atomic operations to manage session state across concurrent operations.
Checking Authentication Status
Verify if a session is active:
if ( SessionContext . isAuthenticated ()) {
// Proceed with HL7 operations
}
Implementation (SessionContext.java:25-28):
public static boolean isAuthenticated () {
SessionState state = STATE . get ();
return state . token () != null && ! state . token (). isEmpty ();
}
Automatic Token Refresh
SessionRefreshManager
The SessionRefreshManager automatically refreshes authentication tokens before expiration:
Key Features:
Automatic scheduling 2 minutes before token expiration
Daemon thread execution (non-blocking)
Automatic rescheduling after successful refresh
Session cleanup on refresh failure
Refresh Timing Calculation (SessionRefreshManager.java:85-96):
private static long calculateDelaySeconds ( String tokenExp) {
LocalDateTime expTime = LocalDateTime . parse (tokenExp, EXP_FORMAT);
LocalDateTime refreshTime = expTime . minus (REFRESH_BEFORE);
long delay = Duration
. between ( LocalDateTime . now (), refreshTime)
. getSeconds ();
return Math . max (delay, 5 ); // Minimum 5 seconds
}
The refresh manager uses a 2-minute buffer (REFRESH_BEFORE = Duration.ofMinutes(2)) to ensure the token is refreshed before expiration.
Refresh Process
The refresh endpoint is called automatically:
private void doRefresh () {
if ( ! SessionContext . isAuthenticated ()) {
throw new IllegalStateException ( "No hay sesión activa para refrescar" );
}
String url = EnvironmentConfig . getAuthRefreshUrl ( SessionContext . getEnvironment ());
String body = JsonUtil . toJson ( SessionContext . getDevice ());
Map < String , String > headers = new HashMap <>();
headers . put ( "Authorization" , "Bearer " + SessionContext . getToken ());
ApiResponse response = apiClient . post (url, body, headers);
// ... error handling and session update
}
Refresh Success:
SessionContext . updateAuth (
refreshResponse . getToken (),
refreshResponse . getExp (),
refreshResponse . getModelEspecifico ()
);
SessionRefreshManager . ensureStarted ( this );
Refresh Failure:
On HTTP 401/403 or any error, the session is automatically cleared:
logout (); // Clear session
throw new AuthProblemException ( "Credenciales inválidas en refresh (HTTP " + status + ")" );
Logout
Manual Logout
Explicitly terminate the current session:
public void logout () {
SessionRefreshManager . stop ();
SessionContext . clear ();
}
Actions Performed:
Stop the automatic refresh scheduler
Clear all session data from SessionContext
Shutdown the refresh daemon thread
Logout is automatically called on refresh failures to prevent unauthorized operations with expired tokens.
Error Handling
AuthProblemException
Thrown when authentication credentials are invalid or tokens cannot be refreshed:
public class AuthProblemException extends RuntimeException {
public AuthProblemException ( String message ) {
super (message);
}
}
Scenarios:
Invalid credentials during login
Token expired and cannot be refreshed (HTTP 401/403)
Token revoked by the server
Handling Example:
try {
authService . login (email, password, apiKey, environment);
} catch ( AuthProblemException e ) {
// Credentials are invalid - prompt user to re-enter
logger . error ( "Authentication failed: " + e . getMessage ());
} catch ( RuntimeException e ) {
// Technical error - network, server, etc.
logger . error ( "Technical error during login: " + e . getMessage ());
}
Login Error Scenarios
HTTP Error Response:
if ( response . isHttpError ()) {
throw new RuntimeException (
"Error técnico durante login (HTTP " + response . getStatusCode () + ")"
);
}
Empty Response:
if ( response . getBody () == null || response . getBody (). isEmpty ()) {
throw new RuntimeException (
"Respuesta vacía del servicio de autenticación"
);
}
Invalid Response:
if (loginResponse == null || loginResponse . getToken () == null ) {
throw new RuntimeException (
"Respuesta inválida del servicio de autenticación"
);
}
Usage Example
import com.hl7client.config.Environment;
import com.hl7client.service.AuthService;
import com.hl7client.client.AuthProblemException;
public class AuthenticationExample {
public static void main ( String [] args ) {
AuthService authService = new AuthService ();
String email = "[email protected] " ;
char [] password = "secure_password" . toCharArray ();
String apiKey = "your-api-key" ;
Environment environment = Environment . SANDBOX ;
try {
// Perform login
authService . login (email, password, apiKey, environment);
System . out . println ( "Authentication successful!" );
// Session is now active and automatically refreshed
// Perform HL7 operations...
// When done, logout
authService . logout ();
System . out . println ( "Logged out successfully" );
} catch ( AuthProblemException e ) {
System . err . println ( "Invalid credentials: " + e . getMessage ());
} catch ( RuntimeException e ) {
System . err . println ( "Technical error: " + e . getMessage ());
} finally {
// Clear password from memory
java . util . Arrays . fill (password, ' \0 ' );
}
}
}
Best Practices
Always use char[] for passwords instead of String to allow explicit memory clearing: char [] password = getPasswordFromUser ();
try {
authService . login (email, password, apiKey, environment);
} finally {
Arrays . fill (password, ' \0 ' ); // Clear from memory
}
Session Lifecycle Management
The session is automatically managed, but you should explicitly logout when:
User logs out manually
Application is shutting down
Switching between provider accounts
// Register shutdown hook
Runtime . getRuntime (). addShutdownHook ( new Thread (() -> {
authService . logout ();
}));
Handle AuthProblemException separately from technical errors: catch ( AuthProblemException e ) {
// Invalid credentials - require user action
promptForCredentials ();
} catch ( RuntimeException e ) {
// Technical issue - retry with exponential backoff
retryWithBackoff ();
}
Environment Configuration
Always use the appropriate environment:
Environment.DEV - Development
Environment.QA - Quality assurance testing
Environment.PRE - Pre-production staging
Environment.PRD - Live production operations
Environment env = isProduction ()
? Environment . PRD
: Environment . DEV ;
See Also