Java Backend SDK Quickstart
Integrate Hawcx OAuth authentication in your Java backend
Hawcx OAuth Client SDK for Java
Add passwordless authentication to your Java backend. Exchange authorization codes for verified user claims and manage MFA.
Installation
The SDK is published to Maven Central as com.hawcx:hawcx-java-sdk. Java 21+ is required.
Maven
<dependency>
<groupId>com.hawcx</groupId>
<artifactId>hawcx-java-sdk</artifactId>
<version>0.2.0</version>
</dependency>Gradle (Kotlin DSL)
dependencies {
implementation("com.hawcx:hawcx-java-sdk:0.2.0")
}Gradle (Groovy)
dependencies {
implementation 'com.hawcx:hawcx-java-sdk:0.2.0'
}Quick Start
OAuth Code Exchange
The most common flow: exchange authCode + codeVerifier for verified user claims.
import com.google.gson.JsonObject;
import com.hawcx.oauth.HawcxOauthClient;
import com.hawcx.oauth.HawcxOauthConfig;
import com.hawcx.oauth.JwtVerifier;
import com.hawcx.oauth.TokenRequest;
HawcxOauthConfig config = new HawcxOauthConfig(System.getenv("HAWCX_BASE_URL"));
HawcxOauthClient client = new HawcxOauthClient(config);
TokenRequest request = new TokenRequest(
System.getenv("HAWCX_CONFIG_ID"),
authCode,
codeVerifier
);
JwtVerifier.VerifyOptions opts = JwtVerifier.VerifyOptions.builder()
.jwksUrl(config.keysEndpoint())
.audience(System.getenv("HAWCX_CONFIG_ID"))
.issuer(System.getenv("HAWCX_BASE_URL"))
.leewaySeconds(10)
.build();
JsonObject claims = client.exchangeCodeForClaims(request, opts);
String userId = claims.get("sub").getAsString();
String email = claims.has("email") ? claims.get("email").getAsString() : null;If you also need the raw id_token (for example, to set as a cookie), use exchangeCodeForTokenAndClaims which returns both:
HawcxOauthClient.TokenAndClaims result = client.exchangeCodeForTokenAndClaims(request, opts);
String idToken = result.tokenResponse().getIdToken();
JsonObject claims = result.claims();Hawcx Delegation Client (MFA Setup)
Optional: advanced flows only
The delegation client is only required for backend-driven MFA setup/verify, suggested-MFA policy, device management, and step-up authentication. If your backend only needs the OAuth code exchange above, you can skip this entirely.
For advanced use cases like setting up MFA for users programmatically:
import com.google.gson.JsonObject;
import com.hawcx.delegation.DelegationClient;
import com.hawcx.delegation.MfaMethod;
DelegationClient client = DelegationClient.fromSecretKey(
System.getenv("HAWCX_BASE_URL"),
System.getenv("HAWCX_SECRET_KEY"), // hwx_sk_v1_...
System.getenv("HAWCX_CONFIG_ID")
);
// Initiate MFA setup (Email, SMS, or TOTP)
JsonObject result = client.mfa.initiate(
"[email protected]",
MfaMethod.SMS,
"+15551234567",
null
);
String sessionId = result.get("session_id").getAsString();
// Verify OTP and complete MFA setup
client.mfa.verify("[email protected]", sessionId, "123456");
// Get user credentials
JsonObject creds = client.users.getCredentials("[email protected]");
String mfaMethod = creds.has("mfa_method") ? creds.get("mfa_method").getAsString() : null;All delegation requests are automatically encrypted (ECIES X25519 + AES-GCM) and signed (Ed25519 or RSA-PSS); you only handle plaintext payloads.
Configuration
Environment Variables
For OAuth Code Exchange:
# Config ID, from Admin Console → Project Settings
HAWCX_CONFIG_ID="<Config ID from Admin Console>"
# Base URL, from Admin Console → Project Settings (environment-specific)
HAWCX_BASE_URL="<Base URL from Admin Console>"Where to find these values: Open the Hawcx Admin Console, go to Project Settings, and copy both the Config ID and Base URL. The base URL is unique to your environment; there is no universal default.
For Hawcx Delegation:
HAWCX_SECRET_KEY="hwx_sk_v1_..."
HAWCX_CONFIG_ID="<Config ID from Admin Console>"
HAWCX_BASE_URL="<Base URL from Admin Console>"If you need to supply the underlying signing/encryption keys directly instead of the hwx_sk_v1_... blob, use DelegationClient.fromKeys(baseUrl, KeyMaterial keys, configId) with a KeyMaterial builder.
Integration Examples
Spring Boot
import com.google.gson.JsonObject;
import com.hawcx.oauth.HawcxOauthClient;
import com.hawcx.oauth.HawcxOauthConfig;
import com.hawcx.oauth.JwtVerifier;
import com.hawcx.oauth.TokenRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
public class AuthController {
private final HawcxOauthClient client;
private final JwtVerifier.VerifyOptions verifyOpts;
private final String configId;
public AuthController(
@Value("${hawcx.base-url}") String baseUrl,
@Value("${hawcx.config-id}") String configId) {
this.configId = configId;
HawcxOauthConfig config = new HawcxOauthConfig(baseUrl);
this.client = new HawcxOauthClient(config);
this.verifyOpts = JwtVerifier.VerifyOptions.builder()
.jwksUrl(config.keysEndpoint())
.audience(configId)
.issuer(baseUrl)
.leewaySeconds(10)
.build();
}
public record ExchangeRequest(String authCode, String codeVerifier) {}
@PostMapping("/exchange")
public ResponseEntity<?> exchange(@RequestBody ExchangeRequest body) {
try {
TokenRequest req = new TokenRequest(configId, body.authCode(), body.codeVerifier());
JsonObject claims = client.exchangeCodeForClaims(req, verifyOpts);
return ResponseEntity.ok(java.util.Map.of(
"success", true,
"userId", claims.get("sub").getAsString(),
"email", claims.has("email") ? claims.get("email").getAsString() : null
));
} catch (Exception e) {
return ResponseEntity.status(401).body(java.util.Map.of("error", "Authentication failed"));
}
}
}Quarkus
import com.google.gson.JsonObject;
import com.hawcx.oauth.HawcxOauthClient;
import com.hawcx.oauth.HawcxOauthConfig;
import com.hawcx.oauth.JwtVerifier;
import com.hawcx.oauth.TokenRequest;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@Path("/exchange")
public class AuthResource {
@ConfigProperty(name = "hawcx.base-url") String baseUrl;
@ConfigProperty(name = "hawcx.config-id") String configId;
public record ExchangeRequest(String authCode, String codeVerifier) {}
@POST
@Consumes("application/json")
@Produces("application/json")
public Response exchange(ExchangeRequest body) {
HawcxOauthConfig config = new HawcxOauthConfig(baseUrl);
HawcxOauthClient client = new HawcxOauthClient(config);
JwtVerifier.VerifyOptions opts = JwtVerifier.VerifyOptions.builder()
.jwksUrl(config.keysEndpoint())
.audience(configId)
.issuer(baseUrl)
.leewaySeconds(10)
.build();
try {
TokenRequest req = new TokenRequest(configId, body.authCode(), body.codeVerifier());
JsonObject claims = client.exchangeCodeForClaims(req, opts);
return Response.ok(java.util.Map.of(
"success", true,
"userId", claims.get("sub").getAsString()
)).build();
} catch (Exception e) {
return Response.status(401).entity(java.util.Map.of("error", "Authentication failed")).build();
}
}
}Plain Java (Javalin)
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.hawcx.oauth.HawcxOauthClient;
import com.hawcx.oauth.HawcxOauthConfig;
import com.hawcx.oauth.JwtVerifier;
import com.hawcx.oauth.TokenRequest;
import io.javalin.Javalin;
public class App {
record ExchangeRequest(String authCode, String codeVerifier) {}
public static void main(String[] args) {
String baseUrl = System.getenv("HAWCX_BASE_URL");
String configId = System.getenv("HAWCX_CONFIG_ID");
HawcxOauthConfig config = new HawcxOauthConfig(baseUrl);
HawcxOauthClient client = new HawcxOauthClient(config);
JwtVerifier.VerifyOptions opts = JwtVerifier.VerifyOptions.builder()
.jwksUrl(config.keysEndpoint())
.audience(configId)
.issuer(baseUrl)
.leewaySeconds(10)
.build();
Gson gson = new Gson();
Javalin.create()
.post("/exchange", ctx -> {
ExchangeRequest body = gson.fromJson(ctx.body(), ExchangeRequest.class);
TokenRequest req = new TokenRequest(configId, body.authCode(), body.codeVerifier());
try {
JsonObject claims = client.exchangeCodeForClaims(req, opts);
ctx.json(java.util.Map.of(
"success", true,
"userId", claims.get("sub").getAsString()
));
} catch (Exception e) {
ctx.status(401).json(java.util.Map.of("error", "Authentication failed"));
}
})
.start(8080);
}
}PKCE Support
codeVerifier is required for the exchange. Store it on the client and send it alongside authCode when your backend calls exchangeCodeForClaims(). If your OAuth provider also requires redirect_uri on the token endpoint, use the four-argument TokenRequest constructor:
TokenRequest req = new TokenRequest(configId, authCode, codeVerifier,
"https://app.example.com/auth/callback");Next Steps
- View the complete API reference for advanced features and detailed method signatures
- Set up MFA management for your users
- Join our developer community on Slack for support and updates