Identity-Aware Proxy (IAP) in Java

Introduction

Identity-Aware Proxy (IAP) is a security layer that provides centralized authentication and authorization for applications. In Java, IAP enables secure access control without modifying application code by leveraging context-aware security policies.

Architecture Overview

1. IAP Integration Architecture

User Request → IAP → Authentication → Authorization → Java Application
↓          ↓          ↓              ↓             ↓
Browser   Google IAP   OAuth/SSO   Context Policies   Business Logic
↓          ↓          ↓              ↓             ↓
HTTPS      JWT Token   User Identity   Access Control   @Secure

Setup and Dependencies

1. Maven Dependencies

<properties>
<spring-boot.version>3.1.0</spring-boot.version>
<google-cloud.version>2.28.0</google-cloud.version>
<nimbus-jose.version>9.31</nimbus-jose.version>
</properties>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Google Cloud IAP -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-iap</artifactId>
<version>2.28.0</version>
</dependency>
<!-- JWT Libraries -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose.version}</version>
</dependency>
<!-- OAuth2 Client -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Spring Security OAuth2 Resource Server -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Servlet API for request wrappers -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

Core IAP Implementation

1. IAP Configuration Properties

@Configuration
@ConfigurationProperties(prefix = "iap")
@Data
@Validated
public class IapProperties {
@NotNull
private GoogleCloudConfig googleCloud = new GoogleCloudConfig();
@NotNull
private SecurityConfig security = new SecurityConfig();
@NotNull
private JwtConfig jwt = new JwtConfig();
@Data
public static class GoogleCloudConfig {
private String projectNumber;
private String projectId;
private String backendServiceId;
private String audience;
}
@Data
public static class SecurityConfig {
private boolean enabled = true;
private boolean requireIap = true;
private List<String> allowedDomains = new ArrayList<>();
private List<String> excludedPaths = List.of("/health", "/info", "/public/**");
private String redirectUrl = "/login";
}
@Data
public static class JwtConfig {
private String issuer = "https://cloud.google.com/iap";
private String headerName = "x-goog-iap-jwt-assertion";
private String publicKeyUrl = "https://www.gstatic.com/iap/verify/public_key";
private long clockSkewSeconds = 300;
}
}

2. IAP JWT Token Verifier

@Service
@Slf4j
public class IapTokenVerifier {
private final IapProperties iapProperties;
private final JwtDecoder jwtDecoder;
private final ObjectMapper objectMapper;
public IapTokenVerifier(IapProperties iapProperties, ObjectMapper objectMapper) {
this.iapProperties = iapProperties;
this.objectMapper = objectMapper;
this.jwtDecoder = createJwtDecoder();
}
/**
* Verify IAP JWT token and extract user identity
*/
public IapUserIdentity verifyToken(String jwtToken) {
try {
Jwt jwt = jwtDecoder.decode(jwtToken);
// Validate token claims
validateTokenClaims(jwt);
// Extract user identity
return extractUserIdentity(jwt);
} catch (Exception e) {
log.error("IAP token verification failed", e);
throw new IapAuthenticationException("Token verification failed", e);
}
}
/**
* Verify token from HTTP request headers
*/
public IapUserIdentity verifyRequest(HttpServletRequest request) {
String jwtToken = extractTokenFromRequest(request);
if (jwtToken == null) {
throw new IapAuthenticationException("IAP JWT token not found in request");
}
return verifyToken(jwtToken);
}
/**
* Validate token without throwing exception
*/
public boolean isValidToken(String jwtToken) {
try {
verifyToken(jwtToken);
return true;
} catch (IapAuthenticationException e) {
return false;
}
}
private JwtDecoder createJwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri(iapProperties.getJwt().getPublicKeyUrl()).build();
}
private void validateTokenClaims(Jwt jwt) {
String issuer = jwt.getIssuer().toString();
String audience = getAudienceClaim(jwt);
String expectedAudience = iapProperties.getGoogleCloud().getAudience();
// Validate issuer
if (!iapProperties.getJwt().getIssuer().equals(issuer)) {
throw new IapAuthenticationException("Invalid token issuer: " + issuer);
}
// Validate audience
if (expectedAudience != null && !expectedAudience.equals(audience)) {
throw new IapAuthenticationException("Invalid token audience: " + audience);
}
// Validate expiration
Instant expiration = jwt.getExpiresAt();
if (expiration != null && expiration.isBefore(Instant.now())) {
throw new IapAuthenticationException("Token has expired");
}
log.debug("IAP token validation successful for user: {}", getEmailClaim(jwt));
}
private IapUserIdentity extractUserIdentity(Jwt jwt) {
return IapUserIdentity.builder()
.id(getSubjectClaim(jwt))
.email(getEmailClaim(jwt))
.name(getClaimAsString(jwt, "name"))
.picture(getClaimAsString(jwt, "picture"))
.hd(getClaimAsString(jwt, "hd")) // hosted domain
.issuer(jwt.getIssuer().toString())
.audience(getAudienceClaim(jwt))
.issuedAt(jwt.getIssuedAt())
.expiresAt(jwt.getExpiresAt())
.claims(jwt.getClaims())
.build();
}
private String extractTokenFromRequest(HttpServletRequest request) {
// Check IAP-specific header first
String token = request.getHeader(iapProperties.getJwt().getHeaderName());
// Fallback to Authorization header
if (token == null) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
token = authHeader.substring(7);
}
}
return token;
}
// Helper methods to extract claims
private String getSubjectClaim(Jwt jwt) {
return jwt.getSubject();
}
private String getEmailClaim(Jwt jwt) {
return getClaimAsString(jwt, "email");
}
private String getAudienceClaim(Jwt jwt) {
Object audience = jwt.getClaims().get("aud");
if (audience instanceof String) {
return (String) audience;
} else if (audience instanceof List) {
@SuppressWarnings("unchecked")
List<String> audiences = (List<String>) audience;
return audiences.get(0); // Return first audience
}
return null;
}
private String getClaimAsString(Jwt jwt, String claimName) {
Object claimValue = jwt.getClaims().get(claimName);
return claimValue != null ? claimValue.toString() : null;
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class IapUserIdentity {
private String id;
private String email;
private String name;
private String picture;
private String hd; // hosted domain
private String issuer;
private String audience;
private Instant issuedAt;
private Instant expiresAt;
private Map<String, Object> claims;
public String getDomain() {
if (email != null && email.contains("@")) {
return email.substring(email.indexOf('@') + 1);
}
return null;
}
public boolean isFromDomain(String domain) {
return domain != null && domain.equals(getDomain());
}
}

Spring Security Integration

1. IAP Security Configuration

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@Slf4j
public class IapSecurityConfig {
private final IapProperties iapProperties;
private final IapTokenVerifier iapTokenVerifier;
public IapSecurityConfig(IapProperties iapProperties, 
IapTokenVerifier iapTokenVerifier) {
this.iapProperties = iapProperties;
this.iapTokenVerifier = iapTokenVerifier;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authz -> authz
.requestMatchers(getExcludedPaths()).permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(iapJwtAuthenticationConverter()))
)
.addFilterBefore(iapAuthenticationFilter(), BasicAuthenticationFilter.class)
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint(iapAuthenticationEntryPoint())
);
return http.build();
}
@Bean
public IapAuthenticationFilter iapAuthenticationFilter() {
return new IapAuthenticationFilter(iapTokenVerifier, iapProperties);
}
@Bean
public IapAuthenticationEntryPoint iapAuthenticationEntryPoint() {
return new IapAuthenticationEntryPoint();
}
@Bean
public Converter<Jwt, AbstractAuthenticationToken> iapJwtAuthenticationConverter() {
return new IapJwtAuthenticationConverter();
}
@Bean
public IapSecurityContextRepository iapSecurityContextRepository() {
return new IapSecurityContextRepository(iapTokenVerifier);
}
private String[] getExcludedPaths() {
List<String> excludedPaths = iapProperties.getSecurity().getExcludedPaths();
return excludedPaths.toArray(new String[0]);
}
}
@Component
@Slf4j
public class IapAuthenticationFilter extends OncePerRequestFilter {
private final IapTokenVerifier iapTokenVerifier;
private final IapProperties iapProperties;
public IapAuthenticationFilter(IapTokenVerifier iapTokenVerifier,
IapProperties iapProperties) {
this.iapTokenVerifier = iapTokenVerifier;
this.iapProperties = iapProperties;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// Skip IAP authentication for excluded paths
if (isExcludedPath(request)) {
filterChain.doFilter(request, response);
return;
}
try {
// Verify IAP token and extract user identity
IapUserIdentity userIdentity = iapTokenVerifier.verifyRequest(request);
// Create authentication token
IapAuthenticationToken authentication = new IapAuthenticationToken(userIdentity);
// Set security context
SecurityContextHolder.getContext().setAuthentication(authentication);
log.debug("IAP authentication successful for user: {}", userIdentity.getEmail());
} catch (IapAuthenticationException e) {
log.warn("IAP authentication failed: {}", e.getMessage());
if (iapProperties.getSecurity().isRequireIap()) {
SecurityContextHolder.clearContext();
throw e;
}
// If IAP is not required, continue without authentication
}
filterChain.doFilter(request, response);
}
private boolean isExcludedPath(HttpServletRequest request) {
String path = request.getServletPath();
return iapProperties.getSecurity().getExcludedPaths().stream()
.anyMatch(excludedPath -> {
if (excludedPath.endsWith("/**")) {
String basePath = excludedPath.substring(0, excludedPath.length() - 3);
return path.startsWith(basePath);
}
return path.equals(excludedPath);
});
}
}
@Component
public class IapJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
@Override
public AbstractAuthenticationToken convert(Jwt jwt) {
IapUserIdentity userIdentity = extractUserIdentity(jwt);
return new IapAuthenticationToken(userIdentity);
}
private IapUserIdentity extractUserIdentity(Jwt jwt) {
return IapUserIdentity.builder()
.id(jwt.getSubject())
.email(jwt.getClaimAsString("email"))
.name(jwt.getClaimAsString("name"))
.picture(jwt.getClaimAsString("picture"))
.hd(jwt.getClaimAsString("hd"))
.issuer(jwt.getIssuer().toString())
.audience(jwt.getClaimAsString("aud"))
.issuedAt(jwt.getIssuedAt())
.expiresAt(jwt.getExpiresAt())
.claims(jwt.getClaims())
.build();
}
}
@Component
@Slf4j
public class IapAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
log.warn("Authentication required for: {} - {}", request.getRequestURI(), authException.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("timestamp", Instant.now().toString());
errorResponse.put("status", HttpServletResponse.SC_UNAUTHORIZED);
errorResponse.put("error", "Unauthorized");
errorResponse.put("message", "IAP authentication required");
errorResponse.put("path", request.getRequestURI());
if (authException instanceof IapAuthenticationException iapException) {
errorResponse.put("details", iapException.getMessage());
}
ObjectMapper objectMapper = new ObjectMapper();
response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
}
}
public class IapAuthenticationToken extends AbstractAuthenticationToken {
private final IapUserIdentity userIdentity;
public IapAuthenticationToken(IapUserIdentity userIdentity) {
super(Collections.emptyList());
this.userIdentity = userIdentity;
setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null; // Credentials are handled by IAP
}
@Override
public Object getPrincipal() {
return userIdentity;
}
public IapUserIdentity getUserIdentity() {
return userIdentity;
}
}
@Component
@Slf4j
public class IapSecurityContextRepository implements SecurityContextRepository {
private final IapTokenVerifier iapTokenVerifier;
public IapSecurityContextRepository(IapTokenVerifier iapTokenVerifier) {
this.iapTokenVerifier = iapTokenVerifier;
}
@Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
return SecurityContextHolder.createEmptyContext();
}
@Override
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
// Stateless - no context saving needed
}
@Override
public boolean containsContext(HttpServletRequest request) {
try {
iapTokenVerifier.verifyRequest(request);
return true;
} catch (IapAuthenticationException e) {
return false;
}
}
}

Authorization and Access Control

1. Context-Aware Authorization Service

@Service
@Slf4j
public class IapAuthorizationService {
private final IapProperties iapProperties;
private final AccessPolicyRepository accessPolicyRepository;
public IapAuthorizationService(IapProperties iapProperties,
AccessPolicyRepository accessPolicyRepository) {
this.iapProperties = iapProperties;
this.accessPolicyRepository = accessPolicyRepository;
}
/**
* Check if user has access to the resource
*/
public boolean hasAccess(IapUserIdentity user, HttpServletRequest request) {
String path = request.getRequestURI();
String method = request.getMethod();
// Check domain restrictions
if (!isDomainAllowed(user)) {
log.warn("Domain not allowed for user: {} from domain: {}", 
user.getEmail(), user.getDomain());
return false;
}
// Check access policies
AccessPolicy policy = accessPolicyRepository.findPolicyForPath(path);
if (policy != null) {
return evaluateAccessPolicy(user, policy, method);
}
// Default: allow access if authenticated
return true;
}
/**
* Check if user has specific role
*/
public boolean hasRole(IapUserIdentity user, String role) {
// Extract roles from claims or external source
List<String> userRoles = extractUserRoles(user);
return userRoles.contains(role);
}
/**
* Check if user has permission for specific action
*/
public boolean hasPermission(IapUserIdentity user, String resource, String action) {
Permission permission = Permission.builder()
.resource(resource)
.action(action)
.build();
return evaluatePermission(user, permission);
}
/**
* Get user's accessible resources
*/
public List<String> getAccessibleResources(IapUserIdentity user) {
return accessPolicyRepository.findAccessibleResources(user);
}
/**
* Context-aware access check with additional context
*/
public boolean hasAccessWithContext(IapUserIdentity user, AccessContext context) {
// Implement context-aware access logic
// Consider time, location, device, etc.
// Time-based access
if (context.getAccessTime() != null && !isWithinAccessHours(context.getAccessTime())) {
return false;
}
// Location-based access
if (context.getUserLocation() != null && !isAllowedLocation(context.getUserLocation())) {
return false;
}
// Device-based access
if (context.getUserDevice() != null && !isAllowedDevice(context.getUserDevice())) {
return false;
}
return true;
}
private boolean isDomainAllowed(IapUserIdentity user) {
List<String> allowedDomains = iapProperties.getSecurity().getAllowedDomains();
if (allowedDomains.isEmpty()) {
return true; // No domain restrictions
}
String userDomain = user.getDomain();
return userDomain != null && allowedDomains.contains(userDomain);
}
private boolean evaluateAccessPolicy(IapUserIdentity user, AccessPolicy policy, String method) {
// Check if method is allowed
if (!policy.getAllowedMethods().contains(method)) {
return false;
}
// Check role requirements
if (!policy.getRequiredRoles().isEmpty()) {
List<String> userRoles = extractUserRoles(user);
if (policy.getRequiredRoles().stream().noneMatch(userRoles::contains)) {
return false;
}
}
// Check domain restrictions
if (!policy.getAllowedDomains().isEmpty()) {
String userDomain = user.getDomain();
if (userDomain == null || !policy.getAllowedDomains().contains(userDomain)) {
return false;
}
}
return true;
}
private List<String> extractUserRoles(IapUserIdentity user) {
// Extract roles from JWT claims or external service
@SuppressWarnings("unchecked")
List<String> roles = (List<String>) user.getClaims().get("roles");
return roles != null ? roles : List.of("USER");
}
private boolean evaluatePermission(IapUserIdentity user, Permission permission) {
// Implement permission evaluation logic
// This could integrate with external policy engines
return accessPolicyRepository.hasPermission(user, permission);
}
private boolean isWithinAccessHours(Instant accessTime) {
// Implement time-based access logic
LocalTime time = LocalTime.ofInstant(accessTime, ZoneId.systemDefault());
return !time.isBefore(LocalTime.of(8, 0)) && !time.isAfter(LocalTime.of(18, 0));
}
private boolean isAllowedLocation(String location) {
// Implement location-based access logic
List<String> allowedLocations = List.of("US", "CA", "EU");
return allowedLocations.contains(location.toUpperCase());
}
private boolean isAllowedDevice(String device) {
// Implement device-based access logic
return !"mobile".equalsIgnoreCase(device); // Example: block mobile devices
}
}
@Service
@Slf4j
public class AccessPolicyRepository {
private final Map<String, AccessPolicy> pathPolicies = new ConcurrentHashMap<>();
private final Map<String, List<Permission>> userPermissions = new ConcurrentHashMap<>();
public AccessPolicyRepository() {
initializeDefaultPolicies();
}
public AccessPolicy findPolicyForPath(String path) {
return pathPolicies.entrySet().stream()
.filter(entry -> path.matches(entry.getKey()))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
}
public List<String> findAccessibleResources(IapUserIdentity user) {
return pathPolicies.entrySet().stream()
.filter(entry -> {
AccessPolicy policy = entry.getValue();
return policy.getRequiredRoles().stream()
.anyMatch(role -> hasRole(user, role));
})
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
public boolean hasPermission(IapUserIdentity user, Permission permission) {
List<Permission> permissions = userPermissions.get(user.getEmail());
return permissions != null && permissions.contains(permission);
}
public void addPolicy(String pathPattern, AccessPolicy policy) {
pathPolicies.put(pathPattern, policy);
}
public void grantPermission(String userEmail, Permission permission) {
userPermissions.computeIfAbsent(userEmail, k -> new ArrayList<>())
.add(permission);
}
private void initializeDefaultPolicies() {
// Admin API policies
addPolicy("/api/admin/.*", AccessPolicy.builder()
.allowedMethods(List.of("GET", "POST", "PUT", "DELETE"))
.requiredRoles(List.of("ADMIN"))
.allowedDomains(List.of("company.com"))
.build());
// User API policies
addPolicy("/api/users/.*", AccessPolicy.builder()
.allowedMethods(List.of("GET", "POST", "PUT"))
.requiredRoles(List.of("USER", "ADMIN"))
.allowedDomains(List.of("company.com"))
.build());
// Public API policies
addPolicy("/api/public/.*", AccessPolicy.builder()
.allowedMethods(List.of("GET"))
.requiredRoles(Collections.emptyList())
.allowedDomains(Collections.emptyList())
.build());
}
private boolean hasRole(IapUserIdentity user, String role) {
@SuppressWarnings("unchecked")
List<String> roles = (List<String>) user.getClaims().get("roles");
return roles != null && roles.contains(role);
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccessPolicy {
private List<String> allowedMethods;
private List<String> requiredRoles;
private List<String> allowedDomains;
private List<String> allowedIps;
private Map<String, Object> conditions;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Permission {
private String resource;
private String action;
private Map<String, Object> conditions;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Permission that = (Permission) o;
return Objects.equals(resource, that.resource) && 
Objects.equals(action, that.action);
}
@Override
public int hashCode() {
return Objects.hash(resource, action);
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccessContext {
private Instant accessTime;
private String userLocation;
private String userDevice;
private String clientIp;
private Map<String, Object> additionalContext;
}

Custom Annotations and Aspect-Oriented Security

1. Custom Security Annotations

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IapSecured {
String[] roles() default {};
String[] domains() default {};
String condition() default "";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IapPreAuthorize {
String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IapPostAuthorize {
String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IapContextAware {
String[] requiredContext() default {};
}
@Component
@Aspect
@Slf4j
public class IapSecurityAspect {
private final IapAuthorizationService authorizationService;
public IapSecurityAspect(IapAuthorizationService authorizationService) {
this.authorizationService = authorizationService;
}
@Around("@annotation(iapSecured)")
public Object checkIapSecurity(ProceedingJoinPoint joinPoint, IapSecured iapSecured) throws Throwable {
IapUserIdentity user = getCurrentUser();
if (user == null) {
throw new IapAuthenticationException("User not authenticated");
}
// Check roles
if (iapSecured.roles().length > 0) {
boolean hasRequiredRole = Arrays.stream(iapSecured.roles())
.anyMatch(role -> authorizationService.hasRole(user, role));
if (!hasRequiredRole) {
throw new IapAuthorizationException("Insufficient roles");
}
}
// Check domains
if (iapSecured.domains().length > 0) {
boolean hasRequiredDomain = Arrays.stream(iapSecured.domains())
.anyMatch(domain -> user.isFromDomain(domain));
if (!hasRequiredDomain) {
throw new IapAuthorizationException("Domain not allowed");
}
}
return joinPoint.proceed();
}
@Around("@annotation(iapContextAware)")
public Object checkContextAwareSecurity(ProceedingJoinPoint joinPoint, 
IapContextAware iapContextAware) throws Throwable {
IapUserIdentity user = getCurrentUser();
if (user == null) {
throw new IapAuthenticationException("User not authenticated");
}
// Build access context from method parameters
AccessContext accessContext = buildAccessContext(joinPoint);
if (!authorizationService.hasAccessWithContext(user, accessContext)) {
throw new IapAuthorizationException("Context-based access denied");
}
return joinPoint.proceed();
}
private IapUserIdentity getCurrentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof IapUserIdentity) {
return (IapUserIdentity) authentication.getPrincipal();
}
return null;
}
private AccessContext buildAccessContext(ProceedingJoinPoint joinPoint) {
// Extract context information from method parameters and request
HttpServletRequest request = getCurrentRequest();
return AccessContext.builder()
.accessTime(Instant.now())
.clientIp(getClientIp(request))
.userDevice(request.getHeader("User-Agent"))
.additionalContext(extractMethodContext(joinPoint))
.build();
}
private HttpServletRequest getCurrentRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes servletRequestAttributes) {
return servletRequestAttributes.getRequest();
}
return null;
}
private String getClientIp(HttpServletRequest request) {
if (request == null) return "unknown";
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return request.getRemoteAddr();
}
private Map<String, Object> extractMethodContext(ProceedingJoinPoint joinPoint) {
Map<String, Object> context = new HashMap<>();
// Extract method parameters for context
Object[] args = joinPoint.getArgs();
String[] paramNames = getParameterNames(joinPoint);
if (paramNames != null) {
for (int i = 0; i < args.length; i++) {
context.put(paramNames[i], args[i]);
}
}
return context;
}
private String[] getParameterNames(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return signature.getParameterNames();
}
}

REST API Controllers with IAP Security

1. Secure REST Controllers

@RestController
@RequestMapping("/api")
@Slf4j
public class SecureUserController {
private final UserService userService;
private final IapAuthorizationService authorizationService;
public SecureUserController(UserService userService,
IapAuthorizationService authorizationService) {
this.userService = userService;
this.authorizationService = authorizationService;
}
@GetMapping("/users")
@IapSecured(roles = {"USER", "ADMIN"})
public ResponseEntity<List<User>> getUsers() {
IapUserIdentity currentUser = getCurrentUser();
log.info("User {} accessed users list", currentUser.getEmail());
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
}
@GetMapping("/users/{id}")
@IapSecured(roles = {"USER", "ADMIN"})
public ResponseEntity<User> getUser(@PathVariable String id) {
IapUserIdentity currentUser = getCurrentUser();
// Context-aware authorization
if (!currentUser.getEmail().equals(id) && !authorizationService.hasRole(currentUser, "ADMIN")) {
throw new IapAuthorizationException("Cannot access other user's data");
}
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
@PostMapping("/users")
@IapSecured(roles = {"ADMIN"}, domains = {"company.com"})
public ResponseEntity<User> createUser(@RequestBody CreateUserRequest request) {
IapUserIdentity currentUser = getCurrentUser();
log.info("Admin {} creating user: {}", currentUser.getEmail(), request.getEmail());
User user = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@PutMapping("/users/{id}")
@IapSecured(roles = {"ADMIN"})
@IapContextAware(requiredContext = {"businessHours"})
public ResponseEntity<User> updateUser(@PathVariable String id, 
@RequestBody UpdateUserRequest request) {
IapUserIdentity currentUser = getCurrentUser();
User user = userService.updateUser(id, request);
return ResponseEntity.ok(user);
}
@GetMapping("/profile")
public ResponseEntity<UserProfile> getProfile() {
IapUserIdentity currentUser = getCurrentUser();
UserProfile profile = userService.getUserProfile(currentUser.getEmail());
return ResponseEntity.ok(profile);
}
@GetMapping("/admin/stats")
@IapSecured(roles = {"ADMIN"}, domains = {"company.com"})
public ResponseEntity<AdminStats> getAdminStats() {
IapUserIdentity currentUser = getCurrentUser();
log.info("Admin {} accessed statistics", currentUser.getEmail());
AdminStats stats = userService.getAdminStatistics();
return ResponseEntity.ok(stats);
}
@GetMapping("/context-test")
@IapContextAware(requiredContext = {"location=US", "device=desktop"})
public ResponseEntity<String> contextAwareEndpoint() {
return ResponseEntity.ok("Context-aware access granted");
}
private IapUserIdentity getCurrentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof IapUserIdentity) {
return (IapUserIdentity) authentication.getPrincipal();
}
throw new IapAuthenticationException("User not authenticated");
}
}
@RestController
@RequestMapping("/api/public")
@Slf4j
public class PublicController {
@GetMapping("/health")
public ResponseEntity<Map<String, String>> health() {
Map<String, String> status = new HashMap<>();
status.put("status", "UP");
status.put("timestamp", Instant.now().toString());
return ResponseEntity.ok(status);
}
@GetMapping("/info")
public ResponseEntity<Map<String, String>> info() {
Map<String, String> info = new HashMap<>();
info.put("name", "IAP Protected Application");
info.put("version", "1.0.0");
return ResponseEntity.ok(info);
}
}

Monitoring and Auditing

1. IAP Audit Service

```java
@Service
@Slf4j
public class IapAuditService {

private final MeterRegistry meterRegistry;
private final Counter authenticationCounter;
private final Counter authorizationCounter;
public IapAuditService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.authenticationCounter = Counter.builder("iap.authentication.attempts")
.description("IAP authentication attempts")
.register(meterRegistry);
this.authorizationCounter = Counter.builder("iap.authorization.checks")
.description("IAP authorization checks")
.register(meterRegistry);
}
public void logAuthenticationSuccess(IapUserIdentity user, HttpServletRequest request) {
authenticationCounter.increment();
log.info("IAP Authentication SUCCESS - User: {}, IP: {}, Path: {}", 
user.getEmail(), getClientIp(request), request.getRequestURI());
// Record detailed metrics
meterRegistry.counter("iap.authentication.success",
"user", user.getEmail(),
"domain", user.getDomain())
.increment();
}
public void logAuthenticationFailure(String reason, HttpServletRequest request) {
authenticationCounter.increment();
log.warn("IAP Authentication FAILURE - Reason: {}, IP: {}, Path: {}", 
reason, getClientIp(request), request.getRequestURI());
meterRegistry.counter("iap.authentication.failure",
"reason", reason)
.increment();
}
public void logAuthorizationSuccess(IapUserIdentity user, String resource, String action) {
authorizationCounter.increment();
log.debug("IAP Authorization SUCCESS - User: {}, Resource: {}, Action: {}", 
user.getEmail(), resource, action);
meterRegistry.counter("iap.authorization.success",
"user", user.getEmail(),
"resource", resource,
"action", action)
.increment();
}
public void logAuthorizationFailure(IapUserIdentity user, String resource, String action, String reason) {
authorizationCounter.increment();
log.warn("IAP Authorization FAILURE - User: {}, Resource: {}, Action: {}, Reason: {}", 
user.getEmail(), resource, action, reason);
meterRegistry.counter("iap.authorization.failure",
"user", user.getEmail(),
"resource", resource,
"action", action,
"reason", reason)
.increment

Java Logistics, Shipping Integration & Enterprise Inventory Automation (Tracking, ERP, RFID & Billing Systems)

https://macronepal.com/blog/aftership-tracking-in-java-enterprise-package-visibility/
Explains how to integrate AfterShip tracking services into Java applications to provide real-time shipment visibility, delivery status updates, and centralized tracking across multiple courier services.

https://macronepal.com/blog/shipping-integration-using-fedex-api-with-java-for-logistics-automation/
Explains how to integrate the FedEx API into Java systems to automate shipping tasks such as creating shipments, calculating delivery costs, generating shipping labels, and tracking packages.

https://macronepal.com/blog/shipping-and-logistics-integrating-ups-apis-with-java-applications/
Explains UPS API integration in Java to enable automated shipping operations including rate calculation, shipment scheduling, tracking, and delivery confirmation management.

https://macronepal.com/blog/generating-and-reading-qr-codes-for-products-in-java/
Explains how Java applications generate and read QR codes for product identification, tracking, and authentication, supporting faster inventory handling and product verification processes.

https://macronepal.com/blog/designing-a-robust-pick-and-pack-workflow-in-java/
Explains how to design an efficient pick-and-pack workflow in Java warehouse systems, covering order processing, item selection, packaging steps, and logistics preparation to improve fulfillment efficiency.

https://macronepal.com/blog/rfid-inventory-management-system-in-java-a-complete-guide/
Explains how RFID technology integrates with Java applications to automate inventory tracking, reduce manual errors, and enable real-time stock monitoring in warehouses and retail environments.

https://macronepal.com/blog/erp-integration-with-odoo-in-java/
Explains how Java applications connect with Odoo ERP systems to synchronize inventory, orders, customer records, and financial data across enterprise systems.

https://macronepal.com/blog/automated-invoice-generation-creating-professional-excel-invoices-with-apache-poi-in-java/
Explains how to automatically generate professional Excel invoices in Java using Apache POI, enabling structured billing documents and automated financial record creation.

https://macronepal.com/blog/enterprise-financial-integration-using-quickbooks-api-in-java-applications/
Explains QuickBooks API integration in Java to automate financial workflows such as invoice management, payment tracking, accounting synchronization, and financial reporting.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper