Skip to content

Commit 28bdff3

Browse files
Merge branch 'develop' of github.com:FusionAuth/fusionauth-java-client into lyle/ENG-3375/introspect-and-libs
2 parents c9deee4 + b45722c commit 28bdff3

19 files changed

Lines changed: 490 additions & 12 deletions

src/main/java/io/fusionauth/client/FusionAuthClient.java

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@
218218
import io.fusionauth.domain.api.twoFactor.TwoFactorStartRequest;
219219
import io.fusionauth.domain.api.twoFactor.TwoFactorStartResponse;
220220
import io.fusionauth.domain.api.twoFactor.TwoFactorStatusResponse;
221+
import io.fusionauth.domain.api.twoFactor.TwoFactorStatusRequest;
221222
import io.fusionauth.domain.api.user.ActionRequest;
222223
import io.fusionauth.domain.api.user.ActionResponse;
223224
import io.fusionauth.domain.api.user.ChangePasswordRequest;
@@ -548,6 +549,26 @@ public ClientResponse<Void, Errors> checkChangePasswordUsingId(String changePass
548549
.go();
549550
}
550551

552+
/**
553+
* Check to see if the user must obtain a Trust Token Id in order to complete a change password request.
554+
* When a user has enabled Two-Factor authentication, before you are allowed to use the Change Password API to change
555+
* your password, you must obtain a Trust Token by completing a Two-Factor Step-Up authentication.
556+
* <p>
557+
* An HTTP status code of 400 with a general error code of [TrustTokenRequired] indicates that a Trust Token is required to make a POST request to this API.
558+
*
559+
* @param changePasswordId The change password Id used to find the user. This value is generated by FusionAuth once the change password workflow has been initiated.
560+
* @param ipAddress (Optional) IP address of the user changing their password. This is used for MFA risk assessment.
561+
* @return The ClientResponse object.
562+
*/
563+
public ClientResponse<Void, Errors> checkChangePasswordUsingIdAndIPAddress(String changePasswordId, String ipAddress) {
564+
return startAnonymous(Void.TYPE, Errors.class)
565+
.uri("/api/user/change-password")
566+
.urlSegment(changePasswordId)
567+
.urlParameter("ipAddress", ipAddress)
568+
.get()
569+
.go();
570+
}
571+
551572
/**
552573
* Check to see if the user must obtain a Trust Token Id in order to complete a change password request.
553574
* When a user has enabled Two-Factor authentication, before you are allowed to use the Change Password API to change
@@ -566,6 +587,26 @@ public ClientResponse<Void, Errors> checkChangePasswordUsingJWT(String encodedJW
566587
.go();
567588
}
568589

590+
/**
591+
* Check to see if the user must obtain a Trust Token Id in order to complete a change password request.
592+
* When a user has enabled Two-Factor authentication, before you are allowed to use the Change Password API to change
593+
* your password, you must obtain a Trust Token by completing a Two-Factor Step-Up authentication.
594+
* <p>
595+
* An HTTP status code of 400 with a general error code of [TrustTokenRequired] indicates that a Trust Token is required to make a POST request to this API.
596+
*
597+
* @param encodedJWT The encoded JWT (access token).
598+
* @param ipAddress (Optional) IP address of the user changing their password. This is used for MFA risk assessment.
599+
* @return The ClientResponse object.
600+
*/
601+
public ClientResponse<Void, Errors> checkChangePasswordUsingJWTAndIPAddress(String encodedJWT, String ipAddress) {
602+
return startAnonymous(Void.TYPE, Errors.class)
603+
.uri("/api/user/change-password")
604+
.authorization("Bearer " + encodedJWT)
605+
.urlParameter("ipAddress", ipAddress)
606+
.get()
607+
.go();
608+
}
609+
569610
/**
570611
* Check to see if the user must obtain a Trust Request Id in order to complete a change password request.
571612
* When a user has enabled Two-Factor authentication, before you are allowed to use the Change Password API to change
@@ -584,6 +625,26 @@ public ClientResponse<Void, Errors> checkChangePasswordUsingLoginId(String login
584625
.go();
585626
}
586627

628+
/**
629+
* Check to see if the user must obtain a Trust Request Id in order to complete a change password request.
630+
* When a user has enabled Two-Factor authentication, before you are allowed to use the Change Password API to change
631+
* your password, you must obtain a Trust Request Id by completing a Two-Factor Step-Up authentication.
632+
* <p>
633+
* An HTTP status code of 400 with a general error code of [TrustTokenRequired] indicates that a Trust Token is required to make a POST request to this API.
634+
*
635+
* @param loginId The loginId (email or username) of the User that you intend to change the password for.
636+
* @param ipAddress (Optional) IP address of the user changing their password. This is used for MFA risk assessment.
637+
* @return The ClientResponse object.
638+
*/
639+
public ClientResponse<Void, Errors> checkChangePasswordUsingLoginIdAndIPAddress(String loginId, String ipAddress) {
640+
return start(Void.TYPE, Errors.class)
641+
.uri("/api/user/change-password")
642+
.urlParameter("loginId", loginId)
643+
.urlParameter("ipAddress", ipAddress)
644+
.get()
645+
.go();
646+
}
647+
587648
/**
588649
* Check to see if the user must obtain a Trust Request Id in order to complete a change password request.
589650
* When a user has enabled Two-Factor authentication, before you are allowed to use the Change Password API to change
@@ -604,6 +665,28 @@ public ClientResponse<Void, Errors> checkChangePasswordUsingLoginIdAndLoginIdTyp
604665
.go();
605666
}
606667

668+
/**
669+
* Check to see if the user must obtain a Trust Request Id in order to complete a change password request.
670+
* When a user has enabled Two-Factor authentication, before you are allowed to use the Change Password API to change
671+
* your password, you must obtain a Trust Request Id by completing a Two-Factor Step-Up authentication.
672+
* <p>
673+
* An HTTP status code of 400 with a general error code of [TrustTokenRequired] indicates that a Trust Token is required to make a POST request to this API.
674+
*
675+
* @param loginId The loginId of the User that you intend to change the password for.
676+
* @param loginIdTypes The identity types that FusionAuth will compare the loginId to.
677+
* @param ipAddress (Optional) IP address of the user changing their password. This is used for MFA risk assessment.
678+
* @return The ClientResponse object.
679+
*/
680+
public ClientResponse<Void, Errors> checkChangePasswordUsingLoginIdAndLoginIdTypesAndIPAddress(String loginId, List<String> loginIdTypes, String ipAddress) {
681+
return start(Void.TYPE, Errors.class)
682+
.uri("/api/user/change-password")
683+
.urlParameter("loginId", loginId)
684+
.urlParameter("loginIdTypes", loginIdTypes)
685+
.urlParameter("ipAddress", ipAddress)
686+
.get()
687+
.go();
688+
}
689+
607690
/**
608691
* Make a Client Credentials grant request to obtain an access token.
609692
*
@@ -4352,6 +4435,24 @@ public ClientResponse<TwoFactorStatusResponse, Errors> retrieveTwoFactorStatus(U
43524435
.go();
43534436
}
43544437

4438+
/**
4439+
* Retrieve a user's two-factor status.
4440+
* <p>
4441+
* This can be used to see if a user will need to complete a two-factor challenge to complete a login,
4442+
* and optionally identify the state of the two-factor trust across various applications. This operation
4443+
* provides more payload options than retrieveTwoFactorStatus.
4444+
*
4445+
* @param request The request object that contains all the information used to check the status.
4446+
* @return The ClientResponse object.
4447+
*/
4448+
public ClientResponse<TwoFactorStatusResponse, Errors> retrieveTwoFactorStatusWithRequest(TwoFactorStatusRequest request) {
4449+
return start(TwoFactorStatusResponse.class, Errors.class)
4450+
.uri("/api/two-factor/status")
4451+
.bodyHandler(new JSONBodyHandler(request, objectMapper()))
4452+
.post()
4453+
.go();
4454+
}
4455+
43554456
/**
43564457
* Retrieves the user for the given Id.
43574458
*

src/main/java/io/fusionauth/domain/Application.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,8 @@ public static class LambdaConfiguration {
454454

455455
public UUID idTokenPopulateId;
456456

457+
public UUID multiFactorRequirementId;
458+
457459
public UUID samlv2PopulateId;
458460

459461
public UUID selfServiceRegistrationValidationId;
@@ -467,6 +469,7 @@ public LambdaConfiguration() {
467469
public LambdaConfiguration(LambdaConfiguration other) {
468470
this.accessTokenPopulateId = other.accessTokenPopulateId;
469471
this.idTokenPopulateId = other.idTokenPopulateId;
472+
this.multiFactorRequirementId = other.multiFactorRequirementId;
470473
this.samlv2PopulateId = other.samlv2PopulateId;
471474
this.selfServiceRegistrationValidationId = other.selfServiceRegistrationValidationId;
472475
this.userinfoPopulateId = other.userinfoPopulateId;
@@ -483,14 +486,15 @@ public boolean equals(Object o) {
483486
LambdaConfiguration that = (LambdaConfiguration) o;
484487
return Objects.equals(accessTokenPopulateId, that.accessTokenPopulateId) &&
485488
Objects.equals(idTokenPopulateId, that.idTokenPopulateId) &&
489+
Objects.equals(multiFactorRequirementId, that.multiFactorRequirementId) &&
486490
Objects.equals(samlv2PopulateId, that.samlv2PopulateId) &&
487491
Objects.equals(selfServiceRegistrationValidationId, that.selfServiceRegistrationValidationId) &&
488492
Objects.equals(userinfoPopulateId, that.userinfoPopulateId);
489493
}
490494

491495
@Override
492496
public int hashCode() {
493-
return Objects.hash(accessTokenPopulateId, idTokenPopulateId, samlv2PopulateId, selfServiceRegistrationValidationId, userinfoPopulateId);
497+
return Objects.hash(accessTokenPopulateId, idTokenPopulateId, multiFactorRequirementId, samlv2PopulateId, selfServiceRegistrationValidationId, userinfoPopulateId);
494498
}
495499

496500
@Override

src/main/java/io/fusionauth/domain/LambdaType.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019-2024, FusionAuth, All Rights Reserved
2+
* Copyright (c) 2019-2025, FusionAuth, All Rights Reserved
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
*/
2323
@SuppressWarnings("ALL")
2424
public enum LambdaType {
25+
// This is an ordinal enum, so make sure new values are added to the end
2526
// @formatter:off
2627
JWTPopulate("populate", "" +
2728
//language=JavaScript
@@ -496,7 +497,24 @@ public enum LambdaType {
496497
"\n" +
497498
" console.info('Hello World!');" +
498499
"\n" +
499-
"}\n");
500+
"}\n"),
501+
MFARequirement("checkRequired", "" +
502+
//language=JavaScript
503+
"// Check whether MFA is required, for a particular action, user, and application, in a given context.\n" +
504+
"function checkRequired(result, user, registration, context) {\n" +
505+
" // When writing a lambda we've added a few helpers to make life easier.\n" +
506+
" // console.info('Hello World'); # This will create an EventLog of type Information\n" +
507+
" // console.error('Not good.'); # This will create an EventLog of type Error\n" +
508+
" // console.debug('Step 42 completed.'); # This will create an EventLog of type Debug\n" +
509+
" // \n" +
510+
" // To dump an entire object to the EventLog you can use JSON.stringify, for example: \n" +
511+
" // console.info(JSON.stringify(user)); \n" +
512+
"\n" +
513+
" // Happy coding! Perform your MFA requirement check here.\n" +
514+
"\n" +
515+
" console.info('Hello World!');" +
516+
"\n" +
517+
"}\n");
500518
// @formatter:on
501519

502520
private final String example;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2025, FusionAuth, All Rights Reserved
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the License.
15+
*/
16+
package io.fusionauth.domain;
17+
18+
/**
19+
* Communicate various actions/contexts in which multi-factor authentication can be used.
20+
*/
21+
public enum MultiFactorAction {
22+
changePassword,
23+
login,
24+
stepUp
25+
}

src/main/java/io/fusionauth/domain/Tenant.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,12 @@ public JWTConfiguration lookupJWTConfiguration(Application application) {
267267
return jwtConfiguration;
268268
}
269269

270+
/**
271+
* Lookup the login MFA policy
272+
*
273+
* @param application application to examine
274+
* @return policy in effect
275+
*/
270276
@JsonIgnore
271277
public MultiFactorLoginPolicy lookupMultiFactorLoginPolicy(Application application) {
272278
if (application != null && application.multiFactorConfiguration.loginPolicy != null) {

src/main/java/io/fusionauth/domain/TenantLambdaConfiguration.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022-2024, FusionAuth, All Rights Reserved
2+
* Copyright (c) 2022-2025, FusionAuth, All Rights Reserved
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,8 @@
2727
public class TenantLambdaConfiguration implements Buildable<TenantLambdaConfiguration> {
2828
public UUID loginValidationId;
2929

30+
public UUID multiFactorRequirementId;
31+
3032
public UUID scimEnterpriseUserRequestConverterId;
3133

3234
public UUID scimEnterpriseUserResponseConverterId;
@@ -45,6 +47,7 @@ public TenantLambdaConfiguration() {
4547

4648
public TenantLambdaConfiguration(TenantLambdaConfiguration other) {
4749
this.loginValidationId = other.loginValidationId;
50+
this.multiFactorRequirementId = other.multiFactorRequirementId;
4851
this.scimEnterpriseUserRequestConverterId = other.scimEnterpriseUserRequestConverterId;
4952
this.scimEnterpriseUserResponseConverterId = other.scimEnterpriseUserResponseConverterId;
5053
this.scimGroupRequestConverterId = other.scimGroupRequestConverterId;
@@ -63,6 +66,7 @@ public boolean equals(Object o) {
6366
}
6467
TenantLambdaConfiguration that = (TenantLambdaConfiguration) o;
6568
return Objects.equals(loginValidationId, that.loginValidationId) &&
69+
Objects.equals(multiFactorRequirementId, that.multiFactorRequirementId) &&
6670
Objects.equals(scimEnterpriseUserRequestConverterId, that.scimEnterpriseUserRequestConverterId) &&
6771
Objects.equals(scimEnterpriseUserResponseConverterId, that.scimEnterpriseUserResponseConverterId) &&
6872
Objects.equals(scimGroupRequestConverterId, that.scimGroupRequestConverterId) &&
@@ -74,6 +78,7 @@ public boolean equals(Object o) {
7478
@Override
7579
public int hashCode() {
7680
return Objects.hash(loginValidationId,
81+
multiFactorRequirementId,
7782
scimEnterpriseUserRequestConverterId,
7883
scimEnterpriseUserResponseConverterId,
7984
scimGroupRequestConverterId,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2025, FusionAuth, All Rights Reserved
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the License.
15+
*/
16+
package io.fusionauth.domain.api.identity.verify;
17+
18+
/**
19+
* Represent the various states/expectations of a user in the context of starting verification
20+
*/
21+
public enum ExistingUserStrategy {
22+
mustExist,
23+
mustNotExist
24+
}

src/main/java/io/fusionauth/domain/api/identity/verify/VerifyStartRequest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
public class VerifyStartRequest implements Buildable<VerifyStartRequest> {
2828
public UUID applicationId;
2929

30+
/**
31+
* When MustExist (default), loginId/loginIdType must correspond to a user in the tenant. When this is MustNotExist, verification can begin
32+
* without a user, in order to perform verification before user creation.
33+
*/
34+
public ExistingUserStrategy existingUserStrategy = ExistingUserStrategy.mustExist;
35+
3036
public String loginId;
3137

3238
public String loginIdType;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2025, FusionAuth, All Rights Reserved
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an
12+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific
14+
* language governing permissions and limitations under the License.
15+
*/
16+
package io.fusionauth.domain.api.twoFactor;
17+
18+
import java.util.UUID;
19+
20+
import com.inversoft.json.JacksonConstructor;
21+
import io.fusionauth.domain.MultiFactorAction;
22+
import io.fusionauth.domain.api.BaseEventRequest;
23+
24+
/**
25+
* Check the status of two-factor authentication for a user, with more options than on a GET request.
26+
*/
27+
public class TwoFactorStatusRequest extends BaseEventRequest {
28+
// required
29+
public final UUID userId;
30+
31+
public String accessToken;
32+
33+
public MultiFactorAction action = MultiFactorAction.login;
34+
35+
public UUID applicationId;
36+
37+
public String twoFactorTrustId;
38+
39+
public TwoFactorStatusRequest(UUID userId) {
40+
this.userId = userId;
41+
}
42+
43+
@JacksonConstructor
44+
private TwoFactorStatusRequest() {
45+
// will be overridden by Jackson
46+
this.userId = null;
47+
}
48+
}

0 commit comments

Comments
 (0)