Java SDK API Reference
Complete API reference for the Hawcx Java SDK
Java SDK API Reference
All public types live under com.hawcx. Add the dependency:
<dependency>
<groupId>com.hawcx</groupId>
<artifactId>hawcx-java-sdk</artifactId>
<version>0.3.0</version>
</dependency>The SDK exposes two construction paths on HawcxOauthClient:
| Path | Constructor | When to use |
|---|---|---|
| Discovery (recommended) | HawcxOauthClient.fromIssuer(issuerUrl, configId, clientId) | New code. Discovers /.well-known/openid-configuration once at startup and wires a stock-Nimbus IDTokenValidator. |
| Legacy | new HawcxOauthClient(HawcxOauthConfig) | v0.2.x callers. Hits fixed endpoints /oauth2/token and /keys directly; verification through the homegrown JwtVerifier. |
Both modes are thread-safe. Cross-mode method calls raise IllegalStateException
with a message pointing at the correct constructor.
HawcxOauthClient (discovery mode)
import com.hawcx.oauth.HawcxOauthClient;
HawcxOauthClient client = HawcxOauthClient.fromIssuer(
System.getenv("HAWCX_BASE_URL"), // issuer URL
System.getenv("HAWCX_CONFIG_ID"), // → x-config-id header on token exchange
System.getenv("HAWCX_CLIENT_ID")); // → expected aud on id_tokensfromIssuer(String issuerUrl, String configId, String clientId)
Fetches <issuerUrl>/.well-known/openid-configuration, reads token_endpoint,
jwks_uri, and id_token_signing_alg_values_supported, and builds an
IDTokenValidator that enforces signature, iss, aud, exp, and nbf
on every subsequent verifyIdToken call.
| Parameter | Purpose |
|---|---|
issuerUrl | Hawcx issuer URL (e.g. https://api.hawcx.com). Also matched as the iss claim. |
configId | Hawcx tenant routing key. Sent as x-config-id on the token-exchange POST. |
clientId | The value the tenant has configured (Admin Console) as the aud claim on its id_tokens. |
configId and clientId are distinct
configId routes the token exchange (header); clientId is matched against
the aud claim. They are configured separately in the Admin Console and may
be different strings. Passing the same value for both fails verification with
Unexpected JWT audience unless the tenant has configured them identically.
Discovery is bounded at 5s connect + 5s read. An unreachable issuer fails
fast with HawcxOauthException (error code discovery_failed) rather than
hanging the constructor. A malformed discovery document (missing
token_endpoint, jwks_uri, or id_token_signing_alg_values_supported)
fails with invalid_discovery_document. The Nimbus exception is preserved
as getCause().
exchangeCodeForIdToken(String authCode, String codeVerifier)
Exchange the authorization code for the raw id_token (a signed JWT string).
The token has not yet been verified — pair with verifyIdToken.
String idToken = client.exchangeCodeForIdToken(authCode, codeVerifier);exchangeCodeForIdToken(String authCode, String codeVerifier, String redirectUri)
Same as above plus the redirect_uri form field, required by RFC 6749 §4.1.3
when the authorization request registered one. Pass null to omit (only
valid if the authorization request also omitted it).
String idToken = client.exchangeCodeForIdToken(authCode, codeVerifier,
"https://app.example.com/cb");verifyIdToken(String idToken)
Verify the id_token's signature and standard OIDC claims:
iss— matched against the issuer in the resolved discovery documentaud— matched against theclientIdpassed tofromIssuer(notconfigId)exp,nbf— enforced
Returns the parsed claim set. Throws JwtVerificationException on any
failure with the Nimbus exception preserved as getCause().
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
IDTokenClaimsSet claims = client.verifyIdToken(idToken);
String userId = claims.getSubject().getValue();
String email = claims.getStringClaim("email");verifyIdToken(String idToken, String expectedNonce)
Same as above plus a nonce claim check. Pass null to skip the nonce check
(equivalent to the single-arg overload).
IDTokenClaimsSet claims = client.verifyIdToken(idToken, expectedNonce);getDiscoveryMetadata()
The Nimbus OIDCProviderMetadata resolved at construction time. Useful for
diagnostics — confirm which endpoints and signing algs were picked up.
client.getDiscoveryMetadata().getTokenEndpointURI();
client.getDiscoveryMetadata().getJWKSetURI();
client.getDiscoveryMetadata().getIDTokenJWSAlgs();Returns null if the client was constructed via the legacy constructor.
HawcxOauthClient (legacy mode)
For v0.2.x callers; new code should prefer the discovery path above. Thread-safe; construct once and reuse.
import com.hawcx.oauth.HawcxOauthClient;
import com.hawcx.oauth.HawcxOauthConfig;
HawcxOauthConfig config = new HawcxOauthConfig(System.getenv("HAWCX_BASE_URL"));
HawcxOauthClient client = new HawcxOauthClient(config);HawcxOauthConfig exposes two endpoint helpers:
config.tokenEndpoint()→<baseUrl>/oauth2/tokenconfig.keysEndpoint()→<baseUrl>/keys(use this as thejwksUrlforJwtVerifier.VerifyOptions)
exchangeCode(TokenRequest)
Exchange an authorization code for an id_token without verifying it. Useful when you only need the raw token. Most apps should use exchangeCodeForClaims instead.
TokenRequest req = new TokenRequest(configId, authCode, codeVerifier);
TokenResponse resp = client.exchangeCode(req);
String idToken = resp.getIdToken();
String tokenType = resp.getTokenType();
long expiresIn = resp.getExpiresIn();exchangeCodeForClaims(TokenRequest, VerifyOptions)
Exchange the code, verify the returned id_token, and return the claims. The recommended end-to-end entry point.
JsonObject claims = client.exchangeCodeForClaims(req, opts);
String userId = claims.get("sub").getAsString();exchangeCodeForTokenAndClaims(TokenRequest, VerifyOptions)
Same as above but also returns the raw TokenResponse, useful when you need the id_token itself (e.g. to set as a cookie):
HawcxOauthClient.TokenAndClaims result = client.exchangeCodeForTokenAndClaims(req, opts);
String idToken = result.tokenResponse().getIdToken();
JsonObject claims = result.claims();verifyJwt(String token, VerifyOptions)
Standalone JWT verification, useful when you already have a token (e.g. from a session cookie) and just want to validate it.
JsonObject claims = client.verifyJwt(idToken, opts);getKeys(TokenRequest)
Fetch the raw JWKS document for inspection. Most callers don't need this; JwtVerifier fetches and caches JWKS internally via jwksUrl.
KeysResponse keys = client.getKeys(req);
JsonObject jwks = keys.getJwks();TokenRequest
Value object describing a single token exchange.
// Without redirect URI:
new TokenRequest(configId, authCode, codeVerifier);
// With OAuth 2.0 redirect URI (required by some providers):
new TokenRequest(configId, authCode, codeVerifier,
"https://app.example.com/auth/callback");JwtVerifier.VerifyOptions
Builder-style options controlling JWT verification. Build one per audience/issuer pair and reuse.
import com.hawcx.oauth.JwtVerifier;
JwtVerifier.VerifyOptions opts = JwtVerifier.VerifyOptions.builder()
.jwksUrl(config.keysEndpoint()) // OR .publicKeyPem("-----BEGIN PUBLIC KEY-----...")
.audience(System.getenv("HAWCX_CONFIG_ID"))
.issuer(System.getenv("HAWCX_BASE_URL"))
.verifyExp(true) // default true
.leewaySeconds(10) // default 0; tolerate small clock skew
.algorithms(java.util.Set.of(JWSAlgorithm.RS256)) // default: RS256, ES256/384/512, EdDSA
.build();| Method | Default | Notes |
|---|---|---|
jwksUrl(String) | (none) | Either this or publicKeyPem must be set. JWKS is fetched once per TTL window (default 1h) and cached. |
publicKeyPem(String) | (none) | Static PEM key. Use for fixed-key setups or tests. |
audience(String) | not validated | Expected aud claim. Set this in production. |
issuer(String) | not validated | Expected iss claim. Set this in production. |
verifyExp(boolean) | true | Disable only for unit tests. |
leewaySeconds(long) | 0 | Tolerated clock skew, in seconds. |
algorithms(Set<JWSAlgorithm>) | RS256, ES256, ES384, ES512, EdDSA | Only tokens signed with one of these algorithms are accepted. |
JwksCache
In-memory TTL cache for JWKS documents. Defaulted automatically by HawcxOauthClient. Override only if you need a custom TTL or want to share a cache across clients:
import com.hawcx.oauth.JwksCache;
import com.hawcx.oauth.JwtVerifier;
JwksCache cache = new JwksCache(/* ttlSeconds */ 600);
JwtVerifier verifier = new JwtVerifier(cache);
HawcxOauthClient client = new HawcxOauthClient(config, verifier);Delegation Client
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. Apps that only need OAuth code exchange can skip this section.
Backend-driven MFA setup, user management, and device management. Every request is automatically encrypted (ECIES X25519 + AES-GCM) and signed (Ed25519 / RSA-PSS). Construct once and reuse; thread-safe.
DelegationClient.fromSecretKey(baseUrl, secretKey, configId)
import com.hawcx.delegation.DelegationClient;
DelegationClient client = DelegationClient.fromSecretKey(
System.getenv("HAWCX_BASE_URL"),
System.getenv("HAWCX_SECRET_KEY"), // hwx_sk_v1_...
System.getenv("HAWCX_CONFIG_ID")
);Or with a raw KeyMaterial bundle:
DelegationClient client = DelegationClient.fromKeys(
baseUrl,
KeyMaterial.builder()
.spSigningKey(spEd25519PrivateKeyPem)
.spEncryptionKey(spX25519PrivateKeyPem)
.idpVerifyKey(idpEd25519PublicKeyPem)
.idpEncryptionKey(idpX25519PublicKeyPem)
.build(),
configId
);MFA
import com.google.gson.JsonObject;
import com.hawcx.delegation.MfaMethod;
// Initiate (Email / SMS / TOTP)
JsonObject init = client.mfa.initiate(
"[email protected]",
MfaMethod.SMS,
"+15551234567", // null for EMAIL/TOTP
null // optional target method when changing
);
String sessionId = init.get("session_id").getAsString();
// Verify the OTP and complete the change
client.mfa.verify("[email protected]", sessionId, "123456");Users
JsonObject creds = client.users.getCredentials("[email protected]");
// Suggested-MFA preference: "none", "always", or "risk_only"
client.users.setSuggestedMfa("[email protected]", "always");
JsonObject pref = client.users.getSuggestedMfa("[email protected]");Devices
JsonObject devices = client.devices.list("[email protected]");
client.devices.revoke("[email protected]", "device-h2index");
client.devices.unrevoke("[email protected]", "device-h2index");
client.devices.delete("[email protected]", "device-h2index");Generic request
For endpoints not covered by a typed namespace method:
JsonObject body = new JsonObject();
body.addProperty("userid", "[email protected]");
JsonObject response = client.request("/v1/management/users/phone", body);Exception Hierarchy
Every SDK error extends com.hawcx.HawcxException (which extends RuntimeException). Catch the root if you only want one handler:
HawcxException
├── HawcxOauthException : token exchange failures (network, 4xx/5xx, missing id_token)
├── JwtVerificationException : signature / claims validation failures
├── InvalidPublicKeyException : JWKS unreachable, no usable keys, malformed PEM
└── DelegationException : base for delegation errors
├── DelegationCryptoException : ECIES / signature operation failures
├── DelegationRequestException : HTTP failure; carries statusCode + responseBody
└── DelegationResponseException : response signature, timestamp, or decryption checks failedtry {
JsonObject claims = client.exchangeCodeForClaims(req, opts);
} catch (HawcxException e) {
log.error("Hawcx SDK call failed", e);
}