Use email verification instead of executing action for send-verify-email
endpoint
Closes #15190 Add support for `send-verify-email` endpoint to use the `email-verification.ftl` instead of `executeActions.ftl` Also introduce a new parameter `lifespan` to be able to override the default lifespan value (12 hours) Signed-off-by: Lex Cao <lexcao@foxmail.com>
This commit is contained in:
parent
5eb7363ddd
commit
47f7e3e8f1
5 changed files with 245 additions and 53 deletions
|
@ -269,6 +269,30 @@ public interface UserResource {
|
||||||
@Path("send-verify-email")
|
@Path("send-verify-email")
|
||||||
void sendVerifyEmail(@QueryParam("client_id") String clientId);
|
void sendVerifyEmail(@QueryParam("client_id") String clientId);
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("send-verify-email")
|
||||||
|
void sendVerifyEmail(@QueryParam("client_id") String clientId, @QueryParam("redirect_uri") String redirectUri);
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("send-verify-email")
|
||||||
|
void sendVerifyEmail(@QueryParam("lifespan") Integer lifespan);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an email-verification email to the user
|
||||||
|
*
|
||||||
|
* An email contains a link the user can click to verify their email address.
|
||||||
|
* The redirectUri and clientId parameters are optional. The default for the
|
||||||
|
* redirect is the account client. The default for the lifespan is 12 hours.
|
||||||
|
*
|
||||||
|
* @param redirectUri Redirect uri
|
||||||
|
* @param clientId Client id
|
||||||
|
* @param lifespan Number of seconds after which the generated token expires
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("send-verify-email")
|
||||||
|
void sendVerifyEmail(@QueryParam("client_id") String clientId, @QueryParam("redirect_uri") String redirectUri, @QueryParam("lifespan") Integer lifespan);
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("sessions")
|
@Path("sessions")
|
||||||
List<UserSessionRepresentation> getUserSessions();
|
List<UserSessionRepresentation> getUserSessions();
|
||||||
|
|
|
@ -29,6 +29,7 @@ public class VerifyEmailActionToken extends DefaultActionToken {
|
||||||
public static final String TOKEN_TYPE = "verify-email";
|
public static final String TOKEN_TYPE = "verify-email";
|
||||||
|
|
||||||
private static final String JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID = "oasid";
|
private static final String JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID = "oasid";
|
||||||
|
private static final String JSON_FIELD_REDIRECT_URI = "reduri";
|
||||||
|
|
||||||
@JsonProperty(value = JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID)
|
@JsonProperty(value = JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID)
|
||||||
private String originalAuthenticationSessionId;
|
private String originalAuthenticationSessionId;
|
||||||
|
@ -49,4 +50,18 @@ public class VerifyEmailActionToken extends DefaultActionToken {
|
||||||
public void setCompoundOriginalAuthenticationSessionId(String originalAuthenticationSessionId) {
|
public void setCompoundOriginalAuthenticationSessionId(String originalAuthenticationSessionId) {
|
||||||
this.originalAuthenticationSessionId = originalAuthenticationSessionId;
|
this.originalAuthenticationSessionId = originalAuthenticationSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonProperty(value = JSON_FIELD_REDIRECT_URI)
|
||||||
|
public String getRedirectUri() {
|
||||||
|
return (String) getOtherClaims().get(JSON_FIELD_REDIRECT_URI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty(value = JSON_FIELD_REDIRECT_URI)
|
||||||
|
public final void setRedirectUri(String redirectUri) {
|
||||||
|
if (redirectUri == null) {
|
||||||
|
getOtherClaims().remove(JSON_FIELD_REDIRECT_URI);
|
||||||
|
} else {
|
||||||
|
setOtherClaims(JSON_FIELD_REDIRECT_URI, redirectUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
import org.keycloak.models.UserModel.RequiredAction;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
|
import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
||||||
import org.keycloak.services.Urls;
|
import org.keycloak.services.Urls;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||||
|
@ -107,6 +109,13 @@ public class VerifyEmailActionTokenHandler extends AbstractActionTokenHandler<Ve
|
||||||
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
||||||
authSession.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
authSession.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
||||||
|
|
||||||
|
String redirectUri = RedirectUtils.verifyRedirectUri(tokenContext.getSession(), token.getRedirectUri(), authSession.getClient());
|
||||||
|
if (redirectUri != null) {
|
||||||
|
authSession.setAuthNote(AuthenticationManager.SET_REDIRECT_URI_AFTER_REQUIRED_ACTIONS, "true");
|
||||||
|
authSession.setRedirectUri(redirectUri);
|
||||||
|
authSession.setClientNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUri);
|
||||||
|
}
|
||||||
|
|
||||||
event.success();
|
event.success();
|
||||||
|
|
||||||
if (token.getCompoundOriginalAuthenticationSessionId() != null) {
|
if (token.getCompoundOriginalAuthenticationSessionId() != null) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.reactive.NoCache;
|
import org.jboss.resteasy.reactive.NoCache;
|
||||||
import org.keycloak.authentication.RequiredActionProvider;
|
import org.keycloak.authentication.RequiredActionProvider;
|
||||||
import org.keycloak.authentication.actiontoken.execactions.ExecuteActionsActionToken;
|
import org.keycloak.authentication.actiontoken.execactions.ExecuteActionsActionToken;
|
||||||
|
import org.keycloak.authentication.actiontoken.verifyemail.VerifyEmailActionToken;
|
||||||
import org.keycloak.authentication.requiredactions.util.RequiredActionsValidator;
|
import org.keycloak.authentication.requiredactions.util.RequiredActionsValidator;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
@ -858,49 +859,14 @@ public class UserResource {
|
||||||
@Parameter(description = "Required actions the user needs to complete") List<String> actions) {
|
@Parameter(description = "Required actions the user needs to complete") List<String> actions) {
|
||||||
auth.users().requireManage(user);
|
auth.users().requireManage(user);
|
||||||
|
|
||||||
if (user.getEmail() == null) {
|
SendEmailParams result = verifySendEmailParams(redirectUri, clientId, lifespan);
|
||||||
throw ErrorResponse.error("User email missing", Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user.isEnabled()) {
|
|
||||||
throw ErrorResponse.error("User is disabled", Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redirectUri != null && clientId == null) {
|
|
||||||
throw ErrorResponse.error("Client id missing", Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clientId == null) {
|
|
||||||
clientId = Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CollectionUtil.isNotEmpty(actions) && !RequiredActionsValidator.validRequiredActions(session, actions)) {
|
if (CollectionUtil.isNotEmpty(actions) && !RequiredActionsValidator.validRequiredActions(session, actions)) {
|
||||||
throw ErrorResponse.error("Provided invalid required actions", Status.BAD_REQUEST);
|
throw ErrorResponse.error("Provided invalid required actions", Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientModel client = realm.getClientByClientId(clientId);
|
int expiration = Time.currentTime() + result.lifespan;
|
||||||
if (client == null) {
|
ExecuteActionsActionToken token = new ExecuteActionsActionToken(user.getId(), user.getEmail(), expiration, actions, result.redirectUri, result.clientId);
|
||||||
logger.debugf("Client %s doesn't exist", clientId);
|
|
||||||
throw ErrorResponse.error("Client doesn't exist", Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
if (!client.isEnabled()) {
|
|
||||||
logger.debugf("Client %s is not enabled", clientId);
|
|
||||||
throw ErrorResponse.error("Client is not enabled", Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
String redirect;
|
|
||||||
if (redirectUri != null) {
|
|
||||||
redirect = RedirectUtils.verifyRedirectUri(session, redirectUri, client);
|
|
||||||
if (redirect == null) {
|
|
||||||
throw ErrorResponse.error("Invalid redirect uri.", Status.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lifespan == null) {
|
|
||||||
lifespan = realm.getActionTokenGeneratedByAdminLifespan();
|
|
||||||
}
|
|
||||||
int expiration = Time.currentTime() + lifespan;
|
|
||||||
ExecuteActionsActionToken token = new ExecuteActionsActionToken(user.getId(), user.getEmail(), expiration, actions, redirectUri, clientId);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
UriBuilder builder = LoginActionsService.actionTokenProcessor(session.getContext().getUri());
|
UriBuilder builder = LoginActionsService.actionTokenProcessor(session.getContext().getUri());
|
||||||
|
@ -912,7 +878,7 @@ public class UserResource {
|
||||||
.setAttribute(Constants.TEMPLATE_ATTR_REQUIRED_ACTIONS, token.getRequiredActions())
|
.setAttribute(Constants.TEMPLATE_ATTR_REQUIRED_ACTIONS, token.getRequiredActions())
|
||||||
.setRealm(realm)
|
.setRealm(realm)
|
||||||
.setUser(user)
|
.setUser(user)
|
||||||
.sendExecuteActions(link, TimeUnit.SECONDS.toMinutes(lifespan));
|
.sendExecuteActions(link, TimeUnit.SECONDS.toMinutes(result.lifespan));
|
||||||
|
|
||||||
//audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
|
//audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
|
||||||
|
|
||||||
|
@ -934,6 +900,7 @@ public class UserResource {
|
||||||
*
|
*
|
||||||
* @param redirectUri Redirect uri
|
* @param redirectUri Redirect uri
|
||||||
* @param clientId Client id
|
* @param clientId Client id
|
||||||
|
* @param lifespan Number of seconds after which the generated token expires
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Path("send-verify-email")
|
@Path("send-verify-email")
|
||||||
|
@ -942,14 +909,38 @@ public class UserResource {
|
||||||
@Tag(name = KeycloakOpenAPI.Admin.Tags.USERS)
|
@Tag(name = KeycloakOpenAPI.Admin.Tags.USERS)
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "Send an email-verification email to the user An email contains a link the user can click to verify their email address.",
|
summary = "Send an email-verification email to the user An email contains a link the user can click to verify their email address.",
|
||||||
description = "The redirectUri and clientId parameters are optional. The default for the redirect is the account client."
|
description = "The redirectUri, clientId and lifespan parameters are optional. The default for the redirect is the account client. The default for the lifespan is 12 hours"
|
||||||
)
|
)
|
||||||
public Response sendVerifyEmail(
|
public Response sendVerifyEmail(
|
||||||
@Parameter(description = "Redirect uri") @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri,
|
@Parameter(description = "Redirect uri") @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri,
|
||||||
@Parameter(description = "Client id") @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
|
@Parameter(description = "Client id") @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId,
|
||||||
List<String> actions = new LinkedList<>();
|
@Parameter(description = "Number of seconds after which the generated token expires") @QueryParam("lifespan") Integer lifespan) {
|
||||||
actions.add(UserModel.RequiredAction.VERIFY_EMAIL.name());
|
auth.users().requireManage(user);
|
||||||
return executeActionsEmail(redirectUri, clientId, null, actions);
|
|
||||||
|
SendEmailParams result = verifySendEmailParams(redirectUri, clientId, lifespan);
|
||||||
|
|
||||||
|
int expiration = Time.currentTime() + result.lifespan;
|
||||||
|
VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), expiration, null, user.getEmail(), result.clientId);
|
||||||
|
token.setRedirectUri(result.redirectUri);
|
||||||
|
|
||||||
|
String link = LoginActionsService.actionTokenProcessor(session.getContext().getUri())
|
||||||
|
.queryParam("key", token.serialize(session, realm, session.getContext().getUri()))
|
||||||
|
.build(realm.getName()).toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
session
|
||||||
|
.getProvider(EmailTemplateProvider.class)
|
||||||
|
.setRealm(realm)
|
||||||
|
.setUser(user)
|
||||||
|
.sendVerifyEmail(link, TimeUnit.SECONDS.toMinutes(result.lifespan));
|
||||||
|
} catch (EmailException e) {
|
||||||
|
ServicesLogger.LOGGER.failedToSendEmail(e);
|
||||||
|
throw ErrorResponse.error("Failed to send verify email", Status.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).success();
|
||||||
|
|
||||||
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -1048,4 +1039,57 @@ public class UserResource {
|
||||||
rep.setLastAccess(Time.toMillis(clientSession.getTimestamp()));
|
rep.setLastAccess(Time.toMillis(clientSession.getTimestamp()));
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SendEmailParams verifySendEmailParams(String redirectUri, String clientId, Integer lifespan) {
|
||||||
|
if (user.getEmail() == null) {
|
||||||
|
throw ErrorResponse.error("User email missing", Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user.isEnabled()) {
|
||||||
|
throw ErrorResponse.error("User is disabled", Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirectUri != null && clientId == null) {
|
||||||
|
throw ErrorResponse.error("Client id missing", Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientId == null) {
|
||||||
|
clientId = Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientModel client = realm.getClientByClientId(clientId);
|
||||||
|
if (client == null) {
|
||||||
|
logger.debugf("Client %s doesn't exist", clientId);
|
||||||
|
throw ErrorResponse.error("Client doesn't exist", Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
if (!client.isEnabled()) {
|
||||||
|
logger.debugf("Client %s is not enabled", clientId);
|
||||||
|
throw ErrorResponse.error("Client is not enabled", Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirectUri != null) {
|
||||||
|
redirectUri = RedirectUtils.verifyRedirectUri(session, redirectUri, client);
|
||||||
|
if (redirectUri == null) {
|
||||||
|
throw ErrorResponse.error("Invalid redirect uri.", Status.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lifespan == null) {
|
||||||
|
lifespan = realm.getActionTokenGeneratedByAdminLifespan();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SendEmailParams(redirectUri, clientId, lifespan);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SendEmailParams {
|
||||||
|
final String redirectUri;
|
||||||
|
final String clientId;
|
||||||
|
final int lifespan;
|
||||||
|
|
||||||
|
public SendEmailParams(String redirectUri, String clientId, Integer lifespan) {
|
||||||
|
this.redirectUri = redirectUri;
|
||||||
|
this.clientId = clientId;
|
||||||
|
this.lifespan = lifespan;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ import org.keycloak.testsuite.util.ClientBuilder;
|
||||||
import org.keycloak.testsuite.util.GreenMailRule;
|
import org.keycloak.testsuite.util.GreenMailRule;
|
||||||
import org.keycloak.testsuite.util.GroupBuilder;
|
import org.keycloak.testsuite.util.GroupBuilder;
|
||||||
import org.keycloak.testsuite.util.MailUtils;
|
import org.keycloak.testsuite.util.MailUtils;
|
||||||
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
import org.keycloak.testsuite.util.RealmBuilder;
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
import org.keycloak.testsuite.util.RoleBuilder;
|
import org.keycloak.testsuite.util.RoleBuilder;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
@ -2300,9 +2301,7 @@ public class UserTest extends AbstractAdminTest {
|
||||||
public void sendVerifyEmail() throws IOException {
|
public void sendVerifyEmail() throws IOException {
|
||||||
UserRepresentation userRep = new UserRepresentation();
|
UserRepresentation userRep = new UserRepresentation();
|
||||||
userRep.setUsername("user1");
|
userRep.setUsername("user1");
|
||||||
|
|
||||||
String id = createUser(userRep);
|
String id = createUser(userRep);
|
||||||
|
|
||||||
UserResource user = realm.users().get(id);
|
UserResource user = realm.users().get(id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -2353,17 +2352,118 @@ public class UserTest extends AbstractAdminTest {
|
||||||
driver.navigate().to(link);
|
driver.navigate().to(link);
|
||||||
|
|
||||||
proceedPage.assertCurrent();
|
proceedPage.assertCurrent();
|
||||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Verify Email"));
|
assertThat(proceedPage.getInfo(), Matchers.containsString("Confirm validity of e-mail address"));
|
||||||
proceedPage.clickProceedLink();
|
proceedPage.clickProceedLink();
|
||||||
Assert.assertEquals("Your account has been updated.", infoPage.getInfo());
|
|
||||||
|
|
||||||
|
Assert.assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||||
driver.navigate().to("about:blank");
|
driver.navigate().to("about:blank");
|
||||||
|
|
||||||
driver.navigate().to(link); // It should be possible to use the same action token multiple times
|
driver.navigate().to(link);
|
||||||
|
infoPage.assertCurrent();
|
||||||
|
assertEquals("Your email address has been verified already.", infoPage.getInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendVerifyEmailWithRedirect() throws IOException {
|
||||||
|
UserRepresentation userRep = new UserRepresentation();
|
||||||
|
userRep.setEnabled(true);
|
||||||
|
userRep.setUsername("user1");
|
||||||
|
userRep.setEmail("user1@test.com");
|
||||||
|
|
||||||
|
String id = createUser(userRep);
|
||||||
|
|
||||||
|
UserResource user = realm.users().get(id);
|
||||||
|
|
||||||
|
String clientId = "test-app";
|
||||||
|
String redirectUri = OAuthClient.SERVER_ROOT + "/auth/some-page";
|
||||||
|
try {
|
||||||
|
// test that an invalid redirect uri is rejected.
|
||||||
|
user.sendVerifyEmail(clientId, "http://unregistered-uri.com/");
|
||||||
|
fail("Expected failure");
|
||||||
|
} catch (ClientErrorException e) {
|
||||||
|
assertEquals(400, e.getResponse().getStatus());
|
||||||
|
|
||||||
|
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||||
|
Assert.assertEquals("Invalid redirect uri.", error.getErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
user.sendVerifyEmail(clientId, redirectUri);
|
||||||
|
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/send-verify-email", ResourceType.USER);
|
||||||
|
|
||||||
|
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||||
|
|
||||||
|
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||||
|
|
||||||
|
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||||
|
|
||||||
|
driver.navigate().to(link);
|
||||||
|
|
||||||
proceedPage.assertCurrent();
|
proceedPage.assertCurrent();
|
||||||
assertThat(proceedPage.getInfo(), Matchers.containsString("Verify Email"));
|
assertThat(proceedPage.getInfo(), Matchers.containsString("Confirm validity of e-mail address"));
|
||||||
proceedPage.clickProceedLink();
|
proceedPage.clickProceedLink();
|
||||||
Assert.assertEquals("Your account has been updated.", infoPage.getInfo());
|
|
||||||
|
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||||
|
|
||||||
|
String pageSource = driver.getPageSource();
|
||||||
|
Assert.assertTrue(pageSource.contains(redirectUri));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendVerifyEmailWithRedirectAndCustomLifespan() throws IOException {
|
||||||
|
UserRepresentation userRep = new UserRepresentation();
|
||||||
|
userRep.setEnabled(true);
|
||||||
|
userRep.setUsername("user1");
|
||||||
|
userRep.setEmail("user1@test.com");
|
||||||
|
|
||||||
|
String id = createUser(userRep);
|
||||||
|
|
||||||
|
UserResource user = realm.users().get(id);
|
||||||
|
|
||||||
|
final int lifespan = (int) TimeUnit.DAYS.toSeconds(1);
|
||||||
|
String redirectUri = OAuthClient.SERVER_ROOT + "/auth/some-page";
|
||||||
|
try {
|
||||||
|
// test that an invalid redirect uri is rejected.
|
||||||
|
user.sendVerifyEmail("test-app", "http://unregistered-uri.com/", lifespan);
|
||||||
|
fail("Expected failure");
|
||||||
|
} catch (ClientErrorException e) {
|
||||||
|
assertEquals(400, e.getResponse().getStatus());
|
||||||
|
|
||||||
|
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||||
|
Assert.assertEquals("Invalid redirect uri.", error.getErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
user.sendVerifyEmail("test-app", redirectUri, lifespan);
|
||||||
|
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/send-verify-email", ResourceType.USER);
|
||||||
|
|
||||||
|
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||||
|
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||||
|
|
||||||
|
MailUtils.EmailBody body = MailUtils.getBody(message);
|
||||||
|
assertThat(body.getText(), Matchers.containsString("This link will expire within 1 day"));
|
||||||
|
assertThat(body.getHtml(), Matchers.containsString("This link will expire within 1 day"));
|
||||||
|
|
||||||
|
String link = MailUtils.getPasswordResetEmailLink(message);
|
||||||
|
String token = link.substring(link.indexOf("key=") + "key=".length());
|
||||||
|
|
||||||
|
try {
|
||||||
|
final AccessToken accessToken = TokenVerifier.create(token, AccessToken.class).getToken();
|
||||||
|
assertEquals(lifespan, accessToken.getExp() - accessToken.getIat());
|
||||||
|
} catch (VerificationException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
driver.navigate().to(link);
|
||||||
|
|
||||||
|
proceedPage.assertCurrent();
|
||||||
|
assertThat(proceedPage.getInfo(), Matchers.containsString("Confirm validity of e-mail address"));
|
||||||
|
proceedPage.clickProceedLink();
|
||||||
|
|
||||||
|
assertEquals("Your account has been updated.", infoPage.getInfo());
|
||||||
|
|
||||||
|
String pageSource = driver.getPageSource();
|
||||||
|
Assert.assertTrue(pageSource.contains(redirectUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue