AgeOnce Docs
Integration

Java

AgeOnce integration with Java (Spring Boot, Jakarta EE)

Java Integration

Complete AgeOnce integration example with Java.

Dependencies (Maven)

<dependencies>
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>4.4.0</version>
    </dependency>
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>jwks-rsa</artifactId>
        <version>0.22.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.15.0</version>
    </dependency>
</dependencies>

Configuration

// AgeOnceConfig.java
@Configuration
public class AgeOnceConfig {
    
    @Value("${ageonce.client-id}")
    private String clientId;
    
    @Value("${ageonce.client-secret}")
    private String clientSecret;
    
    @Value("${ageonce.redirect-uri}")
    private String redirectUri;
    
    @Value("${ageonce.api-url:https://app.ageonce.com}")
    private String apiUrl;
    
    // Getters...
}

AgeOnce service

// AgeOnceService.java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.SecureRandom;
import java.util.HexFormat;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;

@Service
public class AgeOnceService {
    
    private final AgeOnceConfig config;
    private final HttpClient httpClient;
    private final ObjectMapper objectMapper;
    
    public AgeOnceService(AgeOnceConfig config) {
        this.config = config;
        this.httpClient = HttpClient.newHttpClient();
        this.objectMapper = new ObjectMapper();
    }
    
    public record VerifyUrlResult(String url, String state) {}
    
    public VerifyUrlResult generateVerifyUrl() {
        byte[] randomBytes = new byte[16];
        new SecureRandom().nextBytes(randomBytes);
        String state = HexFormat.of().formatHex(randomBytes);
        
        String url = String.format(
            "%s/verify?client_id=%s&redirect_uri=%s&state=%s",
            config.getApiUrl(),
            config.getClientId(),
            config.getRedirectUri(),
            state
        );
        
        return new VerifyUrlResult(url, state);
    }
    
    public Map<String, Object> exchangeCodeForToken(String code) throws Exception {
        Map<String, String> body = Map.of(
            "client_id", config.getClientId(),
            "client_secret", config.getClientSecret(),
            "code", code,
            "redirect_uri", config.getRedirectUri()
        );
        
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(config.getApiUrl() + "/api/oauth/token"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(body)))
            .build();
        
        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        
        if (response.statusCode() != 200) {
            throw new RuntimeException("Token exchange failed");
        }
        
        return objectMapper.readValue(response.body(), Map.class);
    }
    
    public Map<String, Object> validateToken(String token) throws Exception {
        Map<String, String> body = Map.of("token", token);
        
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(config.getApiUrl() + "/api/oauth/validate"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(body)))
            .build();
        
        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        
        if (response.statusCode() != 200) {
            return Map.of("valid", false);
        }
        
        return objectMapper.readValue(response.body(), Map.class);
    }
}

Spring Boot Controller

// AgeOnceController.java
@Controller
public class AgeOnceController {
    
    private final AgeOnceService ageOnceService;
    
    public AgeOnceController(AgeOnceService ageOnceService) {
        this.ageOnceService = ageOnceService;
    }
    
    @GetMapping("/verify")
    public String verify(HttpSession session) {
        var result = ageOnceService.generateVerifyUrl();
        session.setAttribute("oauth_state", result.state());
        return "redirect:" + result.url();
    }
    
    @GetMapping("/callback")
    public String callback(
            @RequestParam String code,
            @RequestParam String state,
            HttpSession session,
            Model model) {
        
        String expectedState = (String) session.getAttribute("oauth_state");
        
        if (!state.equals(expectedState)) {
            model.addAttribute("error", "Invalid state");
            return "error";
        }
        
        try {
            Map<String, Object> tokenData = ageOnceService.exchangeCodeForToken(code);
            String ageToken = (String) tokenData.get("age_token");
            
            Map<String, Object> result = ageOnceService.validateToken(ageToken);
            
            if (Boolean.TRUE.equals(result.get("valid"))) {
                @SuppressWarnings("unchecked")
                Map<String, Object> payload = (Map<String, Object>) result.get("payload");
                
                if (Boolean.TRUE.equals(payload.get("age_verified"))) {
                    session.setAttribute("age_verified", true);
                    session.setAttribute("min_age", payload.get("min_age"));
                    return "redirect:/protected";
                }
            }
            
            model.addAttribute("error", "Age verification failed");
            return "error";
            
        } catch (Exception e) {
            model.addAttribute("error", e.getMessage());
            return "error";
        }
    }
    
    @GetMapping("/protected")
    public String protectedPage(HttpSession session, Model model) {
        if (!Boolean.TRUE.equals(session.getAttribute("age_verified"))) {
            return "redirect:/verify";
        }
        
        return "protected";
    }
}

// AgeVerificationInterceptor.java
@Component
public class AgeVerificationInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
            throws Exception {
        
        HttpSession session = request.getSession();
        
        if (!Boolean.TRUE.equals(session.getAttribute("age_verified"))) {
            response.sendRedirect("/verify");
            return false;
        }
        
        return true;
    }
}

// WebConfig.java
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Autowired
    private AgeVerificationInterceptor ageVerificationInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(ageVerificationInterceptor)
            .addPathPatterns("/protected/**");
    }
}
// AgeOnceServlet.java
@WebServlet("/verify")
public class VerifyServlet extends HttpServlet {
    
    @Inject
    private AgeOnceService ageOnceService;
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        var result = ageOnceService.generateVerifyUrl();
        
        HttpSession session = request.getSession();
        session.setAttribute("oauth_state", result.state());
        
        response.sendRedirect(result.url());
    }
}

@WebServlet("/callback")
public class CallbackServlet extends HttpServlet {
    
    @Inject
    private AgeOnceService ageOnceService;
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        String code = request.getParameter("code");
        String state = request.getParameter("state");
        
        HttpSession session = request.getSession();
        String expectedState = (String) session.getAttribute("oauth_state");
        
        if (!state.equals(expectedState)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid state");
            return;
        }
        
        try {
            Map<String, Object> tokenData = ageOnceService.exchangeCodeForToken(code);
            String ageToken = (String) tokenData.get("age_token");
            
            Map<String, Object> result = ageOnceService.validateToken(ageToken);
            
            if (Boolean.TRUE.equals(result.get("valid"))) {
                @SuppressWarnings("unchecked")
                Map<String, Object> payload = (Map<String, Object>) result.get("payload");
                
                if (Boolean.TRUE.equals(payload.get("age_verified"))) {
                    session.setAttribute("age_verified", true);
                    response.sendRedirect(request.getContextPath() + "/protected");
                    return;
                }
            }
            
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Age verification failed");
            
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
        }
    }
}

// AgeVerificationFilter.java
@WebFilter("/protected/*")
public class AgeVerificationFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        HttpSession session = httpRequest.getSession(false);
        
        if (session == null || !Boolean.TRUE.equals(session.getAttribute("age_verified"))) {
            httpResponse.sendRedirect(httpRequest.getContextPath() + "/verify");
            return;
        }
        
        chain.doFilter(request, response);
    }
}

Local JWT validation

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.JwkProviderBuilder;

@Service
public class AgeOnceJwtValidator {
    
    private final JwkProvider jwkProvider;
    
    public AgeOnceJwtValidator(AgeOnceConfig config) {
        this.jwkProvider = new JwkProviderBuilder(config.getApiUrl() + "/api/oauth/jwks")
            .cached(true)
            .build();
    }
    
    public record ValidationResult(boolean valid, Map<String, Object> payload, String error) {}
    
    public ValidationResult validate(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            
            var jwk = jwkProvider.get(jwt.getKeyId());
            var algorithm = Algorithm.RSA256((java.security.interfaces.RSAPublicKey) jwk.getPublicKey(), null);
            
            var verifier = JWT.require(algorithm)
                .withIssuer("ageonce")
                .build();
            
            DecodedJWT verified = verifier.verify(token);
            
            Map<String, Object> payload = Map.of(
                "age_verified", verified.getClaim("age_verified").asBoolean(),
                "min_age", verified.getClaim("min_age").asInt(),
                "verified_at", verified.getClaim("verified_at").asString()
            );
            
            return new ValidationResult(true, payload, null);
            
        } catch (Exception e) {
            return new ValidationResult(false, null, e.getMessage());
        }
    }
}

Java provides strong typing and broad enterprise environment support.

On this page