From b151136de3dba92ee430e11210c395e990cf7f35 Mon Sep 17 00:00:00 2001 From: Lyle Schemmerling Date: Wed, 17 Sep 2025 16:47:59 -0600 Subject: [PATCH 1/3] new optional tenant id param added (breaking) --- .../fusionauth/client/FusionAuthClient.java | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/fusionauth/client/FusionAuthClient.java b/src/main/java/io/fusionauth/client/FusionAuthClient.java index bba6849a..f5886059 100644 --- a/src/main/java/io/fusionauth/client/FusionAuthClient.java +++ b/src/main/java/io/fusionauth/client/FusionAuthClient.java @@ -388,14 +388,16 @@ public ClientResponse addUserToFamily(UUID familyId, Fam * @param client_secret (Optional) The client secret. This value will be required if client authentication is enabled. * @param token The access token used to identify the user. * @param user_code The end-user verification code. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse approveDevice(String client_id, String client_secret, String token, String user_code) { + public ClientResponse approveDevice(String client_id, String client_secret, String token, String user_code, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("client_secret", Arrays.asList(client_secret)); parameters.put("token", Arrays.asList(token)); parameters.put("user_code", Arrays.asList(user_code)); + parameters.put("tenantId", Arrays.asList("" + tenantId)); return start(DeviceApprovalResponse.class, Errors.class) .uri("/oauth2/device/approve") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -557,14 +559,16 @@ public ClientResponse checkChangePasswordUsingLoginId(String login * @param client_secret (Optional) The client secret used to authenticate this request. * This parameter is optional when Basic Authorization is used to authenticate this request. * @param scope (Optional) This parameter is used to indicate which target entity you are requesting access. To request access to an entity, use the format target-entity:<target-entity-id>:<roles>. Roles are an optional comma separated list. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse clientCredentialsGrant(String client_id, String client_secret, String scope) { + public ClientResponse clientCredentialsGrant(String client_id, String client_secret, String scope, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("client_secret", Arrays.asList(client_secret)); parameters.put("grant_type", Arrays.asList("client_credentials")); parameters.put("scope", Arrays.asList(scope)); + parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -1826,15 +1830,17 @@ public ClientResponse enableTwoFactor(UUID userId, Tw * This parameter is optional when Basic Authorization is used to authenticate this request. * @param client_secret (Optional) The client secret. This value will be required if client authentication is enabled. * @param redirect_uri The URI to redirect to upon a successful request. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse exchangeOAuthCodeForAccessToken(String code, String client_id, String client_secret, String redirect_uri) { + public ClientResponse exchangeOAuthCodeForAccessToken(String code, String client_id, String client_secret, String redirect_uri, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("code", Arrays.asList(code)); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("client_secret", Arrays.asList(client_secret)); parameters.put("grant_type", Arrays.asList("authorization_code")); parameters.put("redirect_uri", Arrays.asList(redirect_uri)); + parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -1852,9 +1858,10 @@ public ClientResponse exchangeOAuthCodeForAccessToken(S * @param client_secret (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. * @param redirect_uri The URI to redirect to upon a successful request. * @param code_verifier The random string generated previously. Will be compared with the code_challenge sent previously, which allows the OAuth provider to authenticate your app. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse exchangeOAuthCodeForAccessTokenUsingPKCE(String code, String client_id, String client_secret, String redirect_uri, String code_verifier) { + public ClientResponse exchangeOAuthCodeForAccessTokenUsingPKCE(String code, String client_id, String client_secret, String redirect_uri, String code_verifier, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("code", Arrays.asList(code)); parameters.put("client_id", Arrays.asList(client_id)); @@ -1862,6 +1869,7 @@ public ClientResponse exchangeOAuthCodeForAccessTokenUs parameters.put("grant_type", Arrays.asList("authorization_code")); parameters.put("redirect_uri", Arrays.asList(redirect_uri)); parameters.put("code_verifier", Arrays.asList(code_verifier)); + parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -1879,9 +1887,10 @@ public ClientResponse exchangeOAuthCodeForAccessTokenUs * @param client_secret (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. * @param scope (Optional) This parameter is optional and if omitted, the same scope requested during the authorization request will be used. If provided the scopes must match those requested during the initial authorization request. * @param user_code (Optional) The end-user verification code. This code is required if using this endpoint to approve the Device Authorization. + * @param tenantId (Optional) The Id of the tenant to use for this request. Required if the request is for a universal application. * @return The ClientResponse object. */ - public ClientResponse exchangeRefreshTokenForAccessToken(String refresh_token, String client_id, String client_secret, String scope, String user_code) { + public ClientResponse exchangeRefreshTokenForAccessToken(String refresh_token, String client_id, String client_secret, String scope, String user_code, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("refresh_token", Arrays.asList(refresh_token)); parameters.put("client_id", Arrays.asList(client_id)); @@ -1889,6 +1898,7 @@ public ClientResponse exchangeRefreshTokenForAccessToke parameters.put("grant_type", Arrays.asList("refresh_token")); parameters.put("scope", Arrays.asList(scope)); parameters.put("user_code", Arrays.asList(user_code)); + parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -1921,9 +1931,10 @@ public ClientResponse exchangeRefreshTokenForJWT(Ref * @param client_secret (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. * @param scope (Optional) This parameter is optional and if omitted, the same scope requested during the authorization request will be used. If provided the scopes must match those requested during the initial authorization request. * @param user_code (Optional) The end-user verification code. This code is required if using this endpoint to approve the Device Authorization. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse exchangeUserCredentialsForAccessToken(String username, String password, String client_id, String client_secret, String scope, String user_code) { + public ClientResponse exchangeUserCredentialsForAccessToken(String username, String password, String client_id, String client_secret, String scope, String user_code, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("username", Arrays.asList(username)); parameters.put("password", Arrays.asList(password)); @@ -1932,6 +1943,7 @@ public ClientResponse exchangeUserCredentialsForAccessT parameters.put("grant_type", Arrays.asList("password")); parameters.put("scope", Arrays.asList(scope)); parameters.put("user_code", Arrays.asList(user_code)); + parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -2140,12 +2152,14 @@ public ClientResponse importWebAuthnCredential(WebAuthnCredentialI * * @param client_id The unique client identifier. The client Id is the Id of the FusionAuth Application for which this token was generated. * @param token The access token returned by this OAuth provider as the result of a successful client credentials grant. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse introspectAccessToken(String client_id, String token) { + public ClientResponse introspectAccessToken(String client_id, String token, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("token", Arrays.asList(token)); + parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(IntrospectResponse.class, OAuthError.class) .uri("/oauth2/introspect") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -2157,11 +2171,13 @@ public ClientResponse introspectAccessToken(Stri * Inspect an access token issued as the result of the Client Credentials Grant. * * @param token The access token returned by this OAuth provider as the result of a successful client credentials grant. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse introspectClientCredentialsAccessToken(String token) { + public ClientResponse introspectClientCredentialsAccessToken(String token, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("token", Arrays.asList(token)); + parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(IntrospectResponse.class, OAuthError.class) .uri("/oauth2/introspect") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -4255,15 +4271,17 @@ public ClientResponse retrieveUserByVerificationId(String * @param client_id The client Id. * @param client_secret The client Id. * @param user_code The end-user verification code. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse retrieveUserCode(String client_id, String client_secret, String user_code) { + public ClientResponse retrieveUserCode(String client_id, String client_secret, String user_code, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("client_secret", Arrays.asList(client_secret)); parameters.put("user_code", Arrays.asList(user_code)); return startAnonymous(Void.TYPE, Void.TYPE) .uri("/oauth2/device/user-code") + .urlParameter("tenantId", tenantId) .bodyHandler(new FormDataBodyHandler(parameters)) .get() .go(); @@ -4277,13 +4295,15 @@ public ClientResponse retrieveUserCode(String client_id, String clie * This request will require an API key. * * @param user_code The end-user verification code. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse retrieveUserCodeUsingAPIKey(String user_code) { + public ClientResponse retrieveUserCodeUsingAPIKey(String user_code, UUID tenantId) { Map> parameters = new HashMap<>(); parameters.put("user_code", Arrays.asList(user_code)); return startAnonymous(Void.TYPE, Void.TYPE) .uri("/oauth2/device/user-code") + .urlParameter("tenantId", tenantId) .bodyHandler(new FormDataBodyHandler(parameters)) .get() .go(); @@ -4335,12 +4355,14 @@ public ClientResponse retrieveUserConsents(UUID userI * Call the UserInfo endpoint to retrieve User Claims from the access token issued by FusionAuth. * * @param encodedJWT The encoded JWT (access token). + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse retrieveUserInfoFromAccessToken(String encodedJWT) { + public ClientResponse retrieveUserInfoFromAccessToken(String encodedJWT, UUID tenantId) { return startAnonymous(UserinfoResponse.class, OAuthError.class) .uri("/oauth2/userinfo") .authorization("Bearer " + encodedJWT) + .urlParameter("tenantId", tenantId) .get() .go(); } @@ -5817,13 +5839,15 @@ public ClientResponse upsertEntityGrant(UUID entityId, EntityGrant * * @param user_code The end-user verification code. * @param client_id The client Id. + * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse validateDevice(String user_code, String client_id) { + public ClientResponse validateDevice(String user_code, String client_id, UUID tenantId) { return startAnonymous(Void.TYPE, Void.TYPE) .uri("/oauth2/device/validate") .urlParameter("user_code", user_code) .urlParameter("client_id", client_id) + .urlParameter("tenantId", tenantId) .get() .go(); } From 2dd373cd16fd5f37c348d4c08a82b21d6b278b40 Mon Sep 17 00:00:00 2001 From: Lyle Schemmerling Date: Tue, 23 Sep 2025 12:17:32 -0600 Subject: [PATCH 2/3] validate and libs --- .../fusionauth/client/FusionAuthClient.java | 254 +++++++++++++++--- 1 file changed, 221 insertions(+), 33 deletions(-) diff --git a/src/main/java/io/fusionauth/client/FusionAuthClient.java b/src/main/java/io/fusionauth/client/FusionAuthClient.java index f5886059..e10ea948 100644 --- a/src/main/java/io/fusionauth/client/FusionAuthClient.java +++ b/src/main/java/io/fusionauth/client/FusionAuthClient.java @@ -237,11 +237,22 @@ import io.fusionauth.domain.api.user.VerifyRegistrationRequest; import io.fusionauth.domain.api.user.VerifyRegistrationResponse; import io.fusionauth.domain.oauth2.AccessToken; +import io.fusionauth.domain.oauth2.AccessTokenIntrospectRequest; +import io.fusionauth.domain.oauth2.ClientCredentialsAccessTokenIntrospectRequest; +import io.fusionauth.domain.oauth2.ClientCredentialsGrantRequest; +import io.fusionauth.domain.oauth2.DeviceApprovalRequest; import io.fusionauth.domain.oauth2.DeviceApprovalResponse; import io.fusionauth.domain.oauth2.IntrospectResponse; import io.fusionauth.domain.oauth2.JWKSResponse; +import io.fusionauth.domain.oauth2.OAuthCodeAccessTokenRequest; +import io.fusionauth.domain.oauth2.OAuthCodePKCEAccessTokenRequest; import io.fusionauth.domain.oauth2.OAuthError; +import io.fusionauth.domain.oauth2.RefreshTokenAccessTokenRequest; +import io.fusionauth.domain.oauth2.RetrieveUserCodeRequest; +import io.fusionauth.domain.oauth2.RetrieveUserCodeUsingAPIKeyRequest; +import io.fusionauth.domain.oauth2.UserCredentialsAccessTokenRequest; import io.fusionauth.domain.oauth2.UserinfoResponse; +import io.fusionauth.domain.oauth2.ValidateDeviceRequest; import io.fusionauth.domain.provider.IdentityProviderType; /** @@ -388,16 +399,14 @@ public ClientResponse addUserToFamily(UUID familyId, Fam * @param client_secret (Optional) The client secret. This value will be required if client authentication is enabled. * @param token The access token used to identify the user. * @param user_code The end-user verification code. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse approveDevice(String client_id, String client_secret, String token, String user_code, UUID tenantId) { + public ClientResponse approveDevice(String client_id, String client_secret, String token, String user_code) { Map> parameters = new HashMap<>(); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("client_secret", Arrays.asList(client_secret)); parameters.put("token", Arrays.asList(token)); parameters.put("user_code", Arrays.asList(user_code)); - parameters.put("tenantId", Arrays.asList("" + tenantId)); return start(DeviceApprovalResponse.class, Errors.class) .uri("/oauth2/device/approve") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -405,6 +414,20 @@ public ClientResponse approveDevice(String clien .go(); } + /** + * Approve a device grant. + * + * @param request The request object containing the device approval information and optional tenantId. + * @return The ClientResponse object. + */ + public ClientResponse approveDeviceWithRequest(DeviceApprovalRequest request) { + return start(DeviceApprovalResponse.class, Errors.class) + .uri("/oauth2/device/approve") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Cancels the user action. * @@ -559,16 +582,14 @@ public ClientResponse checkChangePasswordUsingLoginId(String login * @param client_secret (Optional) The client secret used to authenticate this request. * This parameter is optional when Basic Authorization is used to authenticate this request. * @param scope (Optional) This parameter is used to indicate which target entity you are requesting access. To request access to an entity, use the format target-entity:<target-entity-id>:<roles>. Roles are an optional comma separated list. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse clientCredentialsGrant(String client_id, String client_secret, String scope, UUID tenantId) { + public ClientResponse clientCredentialsGrant(String client_id, String client_secret, String scope) { Map> parameters = new HashMap<>(); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("client_secret", Arrays.asList(client_secret)); parameters.put("grant_type", Arrays.asList("client_credentials")); parameters.put("scope", Arrays.asList(scope)); - parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -576,6 +597,20 @@ public ClientResponse clientCredentialsGrant(String cli .go(); } + /** + * Make a Client Credentials grant request to obtain an access token. + * + * @param request The client credentials grant request containing client authentication, scope and optional tenantId. + * @return The ClientResponse object. + */ + public ClientResponse clientCredentialsGrantWithRequest(ClientCredentialsGrantRequest request) { + return startAnonymous(AccessToken.class, OAuthError.class) + .uri("/oauth2/token") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Adds a comment to the user's account. * @@ -1771,6 +1806,40 @@ public ClientResponse deleteWebhook(UUID webhookId) { .go(); } + /** + * Start the Device Authorization flow using form-encoded parameters + * + * @param client_id The unique client identifier. The client Id is the Id of the FusionAuth Application in which you are attempting to authenticate. + * @param client_secret (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. + * @param scope (Optional) A space-delimited string of the requested scopes. Defaults to all scopes configured in the Application's OAuth configuration. + * @return The ClientResponse object. + */ + public ClientResponse deviceAuthorize(String client_id, String client_secret, String scope) { + Map> parameters = new HashMap<>(); + parameters.put("client_id", Arrays.asList(client_id)); + parameters.put("client_secret", Arrays.asList(client_secret)); + parameters.put("scope", Arrays.asList(scope)); + return startAnonymous(DeviceResponse.class, OAuthError.class) + .uri("/oauth2/device_authorize") + .bodyHandler(new FormDataBodyHandler(parameters)) + .post() + .go(); + } + + /** + * Start the Device Authorization flow using a request body + * + * @param request The device authorization request containing client authentication, scope, and optional device metadata. + * @return The ClientResponse object. + */ + public ClientResponse deviceAuthorizeWithRequest(DeviceAuthorizationRequest request) { + return startAnonymous(DeviceResponse.class, OAuthError.class) + .uri("/oauth2/device_authorize") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Disable two-factor authentication for a user. * @@ -1830,17 +1899,15 @@ public ClientResponse enableTwoFactor(UUID userId, Tw * This parameter is optional when Basic Authorization is used to authenticate this request. * @param client_secret (Optional) The client secret. This value will be required if client authentication is enabled. * @param redirect_uri The URI to redirect to upon a successful request. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse exchangeOAuthCodeForAccessToken(String code, String client_id, String client_secret, String redirect_uri, UUID tenantId) { + public ClientResponse exchangeOAuthCodeForAccessToken(String code, String client_id, String client_secret, String redirect_uri) { Map> parameters = new HashMap<>(); parameters.put("code", Arrays.asList(code)); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("client_secret", Arrays.asList(client_secret)); parameters.put("grant_type", Arrays.asList("authorization_code")); parameters.put("redirect_uri", Arrays.asList(redirect_uri)); - parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -1858,10 +1925,9 @@ public ClientResponse exchangeOAuthCodeForAccessToken(S * @param client_secret (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. * @param redirect_uri The URI to redirect to upon a successful request. * @param code_verifier The random string generated previously. Will be compared with the code_challenge sent previously, which allows the OAuth provider to authenticate your app. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse exchangeOAuthCodeForAccessTokenUsingPKCE(String code, String client_id, String client_secret, String redirect_uri, String code_verifier, UUID tenantId) { + public ClientResponse exchangeOAuthCodeForAccessTokenUsingPKCE(String code, String client_id, String client_secret, String redirect_uri, String code_verifier) { Map> parameters = new HashMap<>(); parameters.put("code", Arrays.asList(code)); parameters.put("client_id", Arrays.asList(client_id)); @@ -1869,7 +1935,6 @@ public ClientResponse exchangeOAuthCodeForAccessTokenUs parameters.put("grant_type", Arrays.asList("authorization_code")); parameters.put("redirect_uri", Arrays.asList(redirect_uri)); parameters.put("code_verifier", Arrays.asList(code_verifier)); - parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -1877,6 +1942,36 @@ public ClientResponse exchangeOAuthCodeForAccessTokenUs .go(); } + /** + * Exchanges an OAuth authorization code and code_verifier for an access token. + * Makes a request to the Token endpoint to exchange the authorization code returned from the Authorize endpoint and a code_verifier for an access token. + * + * @param request The PKCE OAuth code access token exchange request. + * @return The ClientResponse object. + */ + public ClientResponse exchangeOAuthCodeForAccessTokenUsingPKCEWithRequest(OAuthCodePKCEAccessTokenRequest request) { + return startAnonymous(AccessToken.class, OAuthError.class) + .uri("/oauth2/token") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + + /** + * Exchanges an OAuth authorization code for an access token. + * Makes a request to the Token endpoint to exchange the authorization code returned from the Authorize endpoint for an access token. + * + * @param request The OAuth code access token exchange request. + * @return The ClientResponse object. + */ + public ClientResponse exchangeOAuthCodeForAccessTokenWithRequest(OAuthCodeAccessTokenRequest request) { + return startAnonymous(AccessToken.class, OAuthError.class) + .uri("/oauth2/token") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Exchange a Refresh Token for an Access Token. * If you will be using the Refresh Token Grant, you will make a request to the Token endpoint to exchange the user’s refresh token for an access token. @@ -1887,10 +1982,9 @@ public ClientResponse exchangeOAuthCodeForAccessTokenUs * @param client_secret (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. * @param scope (Optional) This parameter is optional and if omitted, the same scope requested during the authorization request will be used. If provided the scopes must match those requested during the initial authorization request. * @param user_code (Optional) The end-user verification code. This code is required if using this endpoint to approve the Device Authorization. - * @param tenantId (Optional) The Id of the tenant to use for this request. Required if the request is for a universal application. * @return The ClientResponse object. */ - public ClientResponse exchangeRefreshTokenForAccessToken(String refresh_token, String client_id, String client_secret, String scope, String user_code, UUID tenantId) { + public ClientResponse exchangeRefreshTokenForAccessToken(String refresh_token, String client_id, String client_secret, String scope, String user_code) { Map> parameters = new HashMap<>(); parameters.put("refresh_token", Arrays.asList(refresh_token)); parameters.put("client_id", Arrays.asList(client_id)); @@ -1898,7 +1992,6 @@ public ClientResponse exchangeRefreshTokenForAccessToke parameters.put("grant_type", Arrays.asList("refresh_token")); parameters.put("scope", Arrays.asList(scope)); parameters.put("user_code", Arrays.asList(user_code)); - parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -1906,6 +1999,21 @@ public ClientResponse exchangeRefreshTokenForAccessToke .go(); } + /** + * Exchange a Refresh Token for an Access Token. + * If you will be using the Refresh Token Grant, you will make a request to the Token endpoint to exchange the user’s refresh token for an access token. + * + * @param request The refresh token access token exchange request. + * @return The ClientResponse object. + */ + public ClientResponse exchangeRefreshTokenForAccessTokenWithRequest(RefreshTokenAccessTokenRequest request) { + return startAnonymous(AccessToken.class, OAuthError.class) + .uri("/oauth2/token") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Exchange a refresh token for a new JWT. * @@ -1931,10 +2039,9 @@ public ClientResponse exchangeRefreshTokenForJWT(Ref * @param client_secret (Optional) The client secret. This value may optionally be provided in the request body instead of the Authorization header. * @param scope (Optional) This parameter is optional and if omitted, the same scope requested during the authorization request will be used. If provided the scopes must match those requested during the initial authorization request. * @param user_code (Optional) The end-user verification code. This code is required if using this endpoint to approve the Device Authorization. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse exchangeUserCredentialsForAccessToken(String username, String password, String client_id, String client_secret, String scope, String user_code, UUID tenantId) { + public ClientResponse exchangeUserCredentialsForAccessToken(String username, String password, String client_id, String client_secret, String scope, String user_code) { Map> parameters = new HashMap<>(); parameters.put("username", Arrays.asList(username)); parameters.put("password", Arrays.asList(password)); @@ -1943,7 +2050,6 @@ public ClientResponse exchangeUserCredentialsForAccessT parameters.put("grant_type", Arrays.asList("password")); parameters.put("scope", Arrays.asList(scope)); parameters.put("user_code", Arrays.asList(user_code)); - parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(AccessToken.class, OAuthError.class) .uri("/oauth2/token") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -1951,6 +2057,21 @@ public ClientResponse exchangeUserCredentialsForAccessT .go(); } + /** + * Exchange User Credentials for a Token. + * If you will be using the Resource Owner Password Credential Grant, you will make a request to the Token endpoint to exchange the user’s email and password for an access token. + * + * @param request The user credentials access token exchange request. + * @return The ClientResponse object. + */ + public ClientResponse exchangeUserCredentialsForAccessTokenWithRequest(UserCredentialsAccessTokenRequest request) { + return startAnonymous(AccessToken.class, OAuthError.class) + .uri("/oauth2/token") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Begins the forgot password sequence, which kicks off an email to the user so that they can reset their password. * @@ -2152,14 +2273,12 @@ public ClientResponse importWebAuthnCredential(WebAuthnCredentialI * * @param client_id The unique client identifier. The client Id is the Id of the FusionAuth Application for which this token was generated. * @param token The access token returned by this OAuth provider as the result of a successful client credentials grant. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse introspectAccessToken(String client_id, String token, UUID tenantId) { + public ClientResponse introspectAccessToken(String client_id, String token) { Map> parameters = new HashMap<>(); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("token", Arrays.asList(token)); - parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(IntrospectResponse.class, OAuthError.class) .uri("/oauth2/introspect") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -2167,17 +2286,29 @@ public ClientResponse introspectAccessToken(Stri .go(); } + /** + * Inspect an access token issued as the result of the User based grant such as the Authorization Code Grant, Implicit Grant, the User Credentials Grant or the Refresh Grant. + * + * @param request The access token introspection request. + * @return The ClientResponse object. + */ + public ClientResponse introspectAccessTokenWithRequest(AccessTokenIntrospectRequest request) { + return startAnonymous(IntrospectResponse.class, OAuthError.class) + .uri("/oauth2/introspect") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Inspect an access token issued as the result of the Client Credentials Grant. * * @param token The access token returned by this OAuth provider as the result of a successful client credentials grant. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse introspectClientCredentialsAccessToken(String token, UUID tenantId) { + public ClientResponse introspectClientCredentialsAccessToken(String token) { Map> parameters = new HashMap<>(); parameters.put("token", Arrays.asList(token)); - parameters.put("tenantId", Arrays.asList("" + tenantId)); return startAnonymous(IntrospectResponse.class, OAuthError.class) .uri("/oauth2/introspect") .bodyHandler(new FormDataBodyHandler(parameters)) @@ -2185,6 +2316,20 @@ public ClientResponse introspectClientCredential .go(); } + /** + * Inspect an access token issued as the result of the Client Credentials Grant. + * + * @param request The client credentials access token. + * @return The ClientResponse object. + */ + public ClientResponse introspectClientCredentialsAccessTokenWithRequest(ClientCredentialsAccessTokenIntrospectRequest request) { + return startAnonymous(IntrospectResponse.class, OAuthError.class) + .uri("/oauth2/introspect") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Issue a new access token (JWT) for the requested Application after ensuring the provided JWT is valid. A valid * access token is properly signed and not expired. @@ -4271,17 +4416,15 @@ public ClientResponse retrieveUserByVerificationId(String * @param client_id The client Id. * @param client_secret The client Id. * @param user_code The end-user verification code. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse retrieveUserCode(String client_id, String client_secret, String user_code, UUID tenantId) { + public ClientResponse retrieveUserCode(String client_id, String client_secret, String user_code) { Map> parameters = new HashMap<>(); parameters.put("client_id", Arrays.asList(client_id)); parameters.put("client_secret", Arrays.asList(client_secret)); parameters.put("user_code", Arrays.asList(user_code)); return startAnonymous(Void.TYPE, Void.TYPE) .uri("/oauth2/device/user-code") - .urlParameter("tenantId", tenantId) .bodyHandler(new FormDataBodyHandler(parameters)) .get() .go(); @@ -4295,20 +4438,52 @@ public ClientResponse retrieveUserCode(String client_id, String clie * This request will require an API key. * * @param user_code The end-user verification code. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse retrieveUserCodeUsingAPIKey(String user_code, UUID tenantId) { + public ClientResponse retrieveUserCodeUsingAPIKey(String user_code) { Map> parameters = new HashMap<>(); parameters.put("user_code", Arrays.asList(user_code)); return startAnonymous(Void.TYPE, Void.TYPE) .uri("/oauth2/device/user-code") - .urlParameter("tenantId", tenantId) .bodyHandler(new FormDataBodyHandler(parameters)) .get() .go(); } + /** + * Retrieve a user_code that is part of an in-progress Device Authorization Grant. + *

+ * This API is useful if you want to build your own login workflow to complete a device grant. + *

+ * This request will require an API key. + * + * @param request The user code retrieval request including optional tenantId. + * @return The ClientResponse object. + */ + public ClientResponse retrieveUserCodeUsingAPIKeyWithRequest(RetrieveUserCodeUsingAPIKeyRequest request) { + return startAnonymous(Void.TYPE, Void.TYPE) + .uri("/oauth2/device/user-code") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + + /** + * Retrieve a user_code that is part of an in-progress Device Authorization Grant. + *

+ * This API is useful if you want to build your own login workflow to complete a device grant. + * + * @param request The user code retrieval request. + * @return The ClientResponse object. + */ + public ClientResponse retrieveUserCodeWithRequest(RetrieveUserCodeRequest request) { + return startAnonymous(Void.TYPE, Void.TYPE) + .uri("/oauth2/device/user-code") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Retrieves all the comments for the user with the given Id. * @@ -5839,19 +6014,32 @@ public ClientResponse upsertEntityGrant(UUID entityId, EntityGrant * * @param user_code The end-user verification code. * @param client_id The client Id. - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse validateDevice(String user_code, String client_id, UUID tenantId) { + public ClientResponse validateDevice(String user_code, String client_id) { return startAnonymous(Void.TYPE, Void.TYPE) .uri("/oauth2/device/validate") .urlParameter("user_code", user_code) .urlParameter("client_id", client_id) - .urlParameter("tenantId", tenantId) .get() .go(); } + /** + * Validates the end-user provided user_code from the user-interaction of the Device Authorization Grant. + * If you build your own activation form you should validate the user provided code prior to beginning the Authorization grant. + * + * @param request The device validation request. + * @return The ClientResponse object. + */ + public ClientResponse validateDeviceWithRequest(ValidateDeviceRequest request) { + return startAnonymous(Void.TYPE, Void.TYPE) + .uri("/oauth2/device/validate") + .bodyHandler(new JSONBodyHandler(request, objectMapper())) + .post() + .go(); + } + /** * Validates the provided JWT (encoded JWT string) to ensure the token is valid. A valid access token is properly * signed and not expired. From 0f53e5d7aca8d8b5dac9fd231a36c5a446c8c710 Mon Sep 17 00:00:00 2001 From: Lyle Schemmerling Date: Tue, 23 Sep 2025 12:42:54 -0600 Subject: [PATCH 3/3] fix imports and user info --- src/main/java/io/fusionauth/client/FusionAuthClient.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/fusionauth/client/FusionAuthClient.java b/src/main/java/io/fusionauth/client/FusionAuthClient.java index e10ea948..9aaf4e31 100644 --- a/src/main/java/io/fusionauth/client/FusionAuthClient.java +++ b/src/main/java/io/fusionauth/client/FusionAuthClient.java @@ -242,6 +242,8 @@ import io.fusionauth.domain.oauth2.ClientCredentialsGrantRequest; import io.fusionauth.domain.oauth2.DeviceApprovalRequest; import io.fusionauth.domain.oauth2.DeviceApprovalResponse; +import io.fusionauth.domain.oauth2.DeviceAuthorizationRequest; +import io.fusionauth.domain.oauth2.DeviceResponse; import io.fusionauth.domain.oauth2.IntrospectResponse; import io.fusionauth.domain.oauth2.JWKSResponse; import io.fusionauth.domain.oauth2.OAuthCodeAccessTokenRequest; @@ -4530,14 +4532,12 @@ public ClientResponse retrieveUserConsents(UUID userI * Call the UserInfo endpoint to retrieve User Claims from the access token issued by FusionAuth. * * @param encodedJWT The encoded JWT (access token). - * @param tenantId (Optional) The Id of the tenant to use for this request. * @return The ClientResponse object. */ - public ClientResponse retrieveUserInfoFromAccessToken(String encodedJWT, UUID tenantId) { + public ClientResponse retrieveUserInfoFromAccessToken(String encodedJWT) { return startAnonymous(UserinfoResponse.class, OAuthError.class) .uri("/oauth2/userinfo") .authorization("Bearer " + encodedJWT) - .urlParameter("tenantId", tenantId) .get() .go(); }