diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index c147ee8..bbd90e1 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -20,7 +20,7 @@ jobs:
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
- name: Test and Assemble and ApiDiff with Gradle
- run: ./gradlew assemble apiDiff check jacocoTestReport --continue --console=plain
+ run: ./gradlew assemble check jacocoTestReport --continue --console=plain
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2
with:
diff --git a/build.gradle b/build.gradle
index 40928ee..2b31102 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,87 +29,79 @@ logger.lifecycle("Using version ${version} for ${name} group $group")
import me.champeau.gradle.japicmp.JapicmpTask
-project.afterEvaluate {
- def versions = project.ext.testInJavaVersions
- for (pluginJavaTestVersion in versions) {
- def taskName = "testInJava-${pluginJavaTestVersion}"
- tasks.register(taskName, Test) {
- def versionToUse = taskName.split("-").getAt(1) as Integer
- description = "Runs unit tests on Java version ${versionToUse}."
- project.logger.quiet("Test will be running in ${versionToUse}")
- group = 'verification'
- javaLauncher.set(javaToolchains.launcherFor {
- languageVersion = JavaLanguageVersion.of(versionToUse)
- })
- shouldRunAfter(tasks.named('test'))
- }
- tasks.named('check') {
- dependsOn(taskName)
- }
- }
-
- project.configure(project) {
- def baselineVersion = project.ext.baselineCompareVersion
- task('apiDiff', type: JapicmpTask, dependsOn: 'jar') {
- oldClasspath.from(files(getBaselineJar(project, baselineVersion)))
- newClasspath.from(files(jar.archiveFile))
- onlyModified = true
- failOnModification = true
- ignoreMissingClasses = true
- htmlOutputFile = file("$buildDir/reports/apiDiff/apiDiff.html")
- txtOutputFile = file("$buildDir/reports/apiDiff/apiDiff.txt")
- doLast {
- project.logger.quiet("Comparing against baseline version ${baselineVersion}")
- }
- }
- }
-}
-
-private static File getBaselineJar(Project project, String baselineVersion) {
- // Use detached configuration: https://github.com/square/okhttp/blob/master/build.gradle#L270
- def group = project.group
- try {
- def baseline = "${project.group}:${project.name}:$baselineVersion"
- project.group = 'virtual_group_for_japicmp'
- def dependency = project.dependencies.create(baseline + "@jar")
- return project.configurations.detachedConfiguration(dependency).files.find {
- it.name == "${project.name}-${baselineVersion}.jar"
- }
- } finally {
- project.group = group
- }
-}
+//project.afterEvaluate {
+// def versions = project.ext.testInJavaVersions
+// for (pluginJavaTestVersion in versions) {
+// def taskName = "testInJava-${pluginJavaTestVersion}"
+// tasks.register(taskName, Test) {
+// def versionToUse = taskName.split("-").getAt(1) as Integer
+// description = "Runs unit tests on Java version ${versionToUse}."
+// project.logger.quiet("Test will be running in ${versionToUse}")
+// group = 'verification'
+// javaLauncher.set(javaToolchains.launcherFor {
+// languageVersion = JavaLanguageVersion.of(versionToUse)
+// })
+// shouldRunAfter(tasks.named('test'))
+// }
+// tasks.named('check') {
+// dependsOn(taskName)
+// }
+// }
+//
+// project.configure(project) {
+// def baselineVersion = project.ext.baselineCompareVersion
+// task('apiDiff', type: JapicmpTask, dependsOn: 'jar') {
+// oldClasspath.from(files(getBaselineJar(project, baselineVersion)))
+// newClasspath.from(files(jar.archiveFile))
+// onlyModified = true
+// failOnModification = true
+// ignoreMissingClasses = true
+// htmlOutputFile = file("$buildDir/reports/apiDiff/apiDiff.html")
+// txtOutputFile = file("$buildDir/reports/apiDiff/apiDiff.txt")
+// doLast {
+// project.logger.quiet("Comparing against baseline version ${baselineVersion}")
+// }
+// }
+// }
+//}
+//
+//private static File getBaselineJar(Project project, String baselineVersion) {
+// // Use detached configuration: https://github.com/square/okhttp/blob/master/build.gradle#L270
+// def group = project.group
+// try {
+// def baseline = "${project.group}:${project.name}:$baselineVersion"
+// project.group = 'virtual_group_for_japicmp'
+// def dependency = project.dependencies.create(baseline + "@jar")
+// return project.configurations.detachedConfiguration(dependency).files.find {
+// it.name == "${project.name}-${baselineVersion}.jar"
+// }
+// } finally {
+// project.group = group
+// }
+//}
ext {
baselineCompareVersion = '1.5.0'
- testInJavaVersions = [8, 11, 17, 21]
+ testInJavaVersions = [17, 21]
}
jacocoTestReport {
reports {
- xml.enabled = true
- html.enabled = true
+ xml.required = true
+ html.required = true
}
}
java {
toolchain {
- languageVersion = JavaLanguageVersion.of(8)
- }
- // Needed because of broken gradle metadata, see https://github.com/google/guava/issues/6612#issuecomment-1614992368
- sourceSets.all {
- configurations.getByName(runtimeClasspathConfigurationName) {
- attributes.attribute(Attribute.of("org.gradle.jvm.environment", String), "standard-jvm")
- }
- configurations.getByName(compileClasspathConfigurationName) {
- attributes.attribute(Attribute.of("org.gradle.jvm.environment", String), "standard-jvm")
- }
+ languageVersion = JavaLanguageVersion.of(17)
}
}
+
compileJava {
- sourceCompatibility '1.8'
- targetCompatibility '1.8'
+ sourceCompatibility '17'
+ targetCompatibility '17'
}
test {
@@ -121,21 +113,20 @@ test {
}
dependencies {
- implementation 'javax.servlet:javax.servlet-api:3.1.0'
+ implementation 'jakarta.servlet:jakarta.servlet-api:6.0.0'
implementation 'org.apache.commons:commons-lang3:3.20.0'
- implementation 'com.google.guava:guava-annotations:r03'
+ implementation 'com.google.guava:guava:32.0.1-jre'
implementation 'commons-codec:commons-codec:1.22.0'
- api 'com.auth0:auth0:1.45.1'
- api 'com.auth0:java-jwt:3.19.4'
+ api 'com.auth0:auth0:3.3.0'
+ api 'com.auth0:java-jwt:4.5.0'
api 'com.auth0:jwks-rsa:0.23.0'
- testImplementation 'org.bouncycastle:bcprov-jdk15on:1.70'
- testImplementation 'org.hamcrest:java-hamcrest:2.0.0.0'
- testImplementation 'org.hamcrest:hamcrest-core:1.3'
- testImplementation 'org.mockito:mockito-core:2.28.2'
+ testImplementation 'org.hamcrest:hamcrest:2.2'
+ testImplementation 'org.mockito:mockito-core:4.11.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
- testImplementation 'org.springframework:spring-test:4.3.30.RELEASE'
+ testImplementation 'org.springframework:spring-test:6.0.14'
+ testImplementation 'org.springframework:spring-web:6.0.14'
testImplementation 'com.squareup.okhttp3:okhttp:4.12.0'
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index c51cbf1..e1adfb4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/src/main/java/com/auth0/AuthenticationController.java b/src/main/java/com/auth0/AuthenticationController.java
index 9fc2724..8a14c9a 100644
--- a/src/main/java/com/auth0/AuthenticationController.java
+++ b/src/main/java/com/auth0/AuthenticationController.java
@@ -1,14 +1,12 @@
package com.auth0;
-import com.auth0.client.HttpOptions;
import com.auth0.client.auth.AuthAPI;
import com.auth0.jwk.JwkProvider;
-import com.auth0.net.Telemetry;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.Validate;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
/**
* Base Auth0 Authenticator class.
@@ -33,13 +31,9 @@ RequestProcessor getRequestProcessor() {
}
/**
- * Create a new {@link Builder} instance to configure the
- * {@link AuthenticationController} response type and algorithm used on the
- * verification.
- * By default it will request response type 'code' and later perform the Code
- * Exchange, but if the response type is changed to 'token' it will handle
- * the Implicit Grant using the HS256 algorithm with the Client Secret as
- * secret.
+ * Create a new {@link Builder} instance to configure the {@link AuthenticationController} response type and algorithm used on the verification.
+ * By default it will request response type 'code' and later perform the Code Exchange, but if the response type is changed to 'token' it will handle
+ * the Implicit Grant using the HS256 algorithm with the Client Secret as secret.
*
* @param domain the Auth0 domain
* @param clientId the Auth0 application's client id
@@ -85,7 +79,6 @@ public static class Builder {
private boolean useLegacySameSiteCookie;
private String organization;
private String invitation;
- private HttpOptions httpOptions;
private String cookiePath;
private DomainResolver domainResolver;
@@ -150,21 +143,7 @@ public Builder withDomainResolver(DomainResolver domainResolver) {
}
/**
- * Customize certain aspects of the underlying HTTP client networking library,
- * such as timeouts and proxy configuration.
- *
- * @param httpOptions a non-null {@code HttpOptions}
- * @return this same builder instance.
- */
- public Builder withHttpOptions(HttpOptions httpOptions) {
- Validate.notNull(httpOptions);
- this.httpOptions = httpOptions;
- return this;
- }
-
- /**
- * Specify that transient authentication-based cookies such as state and nonce
- * are created with the specified
+ * Specify that transient authentication-based cookies such as state and nonce are created with the specified
* {@code Path} cookie attribute.
*
* @param cookiePath the path to set on the cookie.
@@ -177,12 +156,9 @@ public Builder withCookiePath(String cookiePath) {
}
/**
- * Change the response type to request in the Authorization step. Default value
- * is 'code'.
+ * Change the response type to request in the Authorization step. Default value is 'code'.
*
- * @param responseType the response type to request. Any combination of 'code',
- * 'token' and 'id_token' but 'token id_token' is allowed,
- * using a space as separator.
+ * @param responseType the response type to request. Any combination of 'code', 'token' and 'id_token' but 'token id_token' is allowed, using a space as separator.
* @return this same builder instance.
*/
public Builder withResponseType(String responseType) {
@@ -192,10 +168,8 @@ public Builder withResponseType(String responseType) {
}
/**
- * Sets the Jwk Provider that will return the Public Key required to verify the
- * token in case of Implicit Grant flows.
- * This is required if the Auth0 Application is signing the tokens with the
- * RS256 algorithm.
+ * Sets the Jwk Provider that will return the Public Key required to verify the token in case of Implicit Grant flows.
+ * This is required if the Auth0 Application is signing the tokens with the RS256 algorithm.
*
* @param jwkProvider a valid Jwk provider.
* @return this same builder instance.
@@ -207,8 +181,7 @@ public Builder withJwkProvider(JwkProvider jwkProvider) {
}
/**
- * Sets the clock-skew or leeway value to use in the ID Token verification. The
- * value must be in seconds.
+ * Sets the clock-skew or leeway value to use in the ID Token verification. The value must be in seconds.
* Defaults to 60 seconds.
*
* @param clockSkew the clock-skew to use for ID Token verification, in seconds.
@@ -221,8 +194,7 @@ public Builder withClockSkew(Integer clockSkew) {
}
/**
- * Sets the allowable elapsed time in seconds since the last time user was
- * authenticated.
+ * Sets the allowable elapsed time in seconds since the last time user was authenticated.
* By default there is no limit.
*
* @param maxAge the max age of the authentication, in seconds.
@@ -235,14 +207,10 @@ public Builder withAuthenticationMaxAge(Integer maxAge) {
}
/**
- * Sets whether fallback cookies will be set for clients that do not support
- * SameSite=None cookie attribute.
- * The SameSite Cookie attribute will only be set to "None" if the reponseType
- * includes "id_token".
+ * Sets whether fallback cookies will be set for clients that do not support SameSite=None cookie attribute.
+ * The SameSite Cookie attribute will only be set to "None" if the reponseType includes "id_token".
* By default this is true.
- *
- * @param useLegacySameSiteCookie whether fallback auth-based cookies should be
- * set.
+ * @param useLegacySameSiteCookie whether fallback auth-based cookies should be set.
* @return this same builder instance.
*/
public Builder withLegacySameSiteCookie(boolean useLegacySameSiteCookie) {
@@ -251,8 +219,7 @@ public Builder withLegacySameSiteCookie(boolean useLegacySameSiteCookie) {
}
/**
- * Sets the organization query string parameter value used to login to an
- * organization.
+ * Sets the organization query string parameter value used to login to an organization.
*
* @param organization The ID or name of the organization to log the user in to.
* @return the builder instance.
@@ -264,12 +231,10 @@ public Builder withOrganization(String organization) {
}
/**
- * Sets the invitation query string parameter to join an organization. If using
- * this, you must also specify the
+ * Sets the invitation query string parameter to join an organization. If using this, you must also specify the
* organization using {@linkplain Builder#withOrganization(String)}.
*
- * @param invitation The ID of the invitation to accept. This is available on
- * the URL that is provided when accepting an invitation.
+ * @param invitation The ID of the invitation to accept. This is available on the URL that is provided when accepting an invitation.
* @return the builder instance.
*/
public Builder withInvitation(String invitation) {
@@ -279,14 +244,10 @@ public Builder withInvitation(String invitation) {
}
/**
- * Create a new {@link AuthenticationController} instance that will handle both
- * Code Grant and Implicit Grant flows using either Code Exchange or Token
- * Signature verification.
+ * Create a new {@link AuthenticationController} instance that will handle both Code Grant and Implicit Grant flows using either Code Exchange or Token Signature verification.
*
* @return a new instance of {@link AuthenticationController}.
- * @throws UnsupportedOperationException if the Implicit Grant is chosen and the
- * environment doesn't support UTF-8
- * encoding.
+ * @throws UnsupportedOperationException if the Implicit Grant is chosen and the environment doesn't support UTF-8 encoding.
*/
public AuthenticationController build() throws UnsupportedOperationException {
validateDomainConfiguration();
@@ -298,7 +259,7 @@ public AuthenticationController build() throws UnsupportedOperationException {
SignatureVerifier signatureVerifier = buildSignatureVerifier();
RequestProcessor processor = new RequestProcessor.Builder(domainProvider, responseType, clientId,
- clientSecret, httpOptions, signatureVerifier)
+ clientSecret, signatureVerifier)
.withClockSkew(clockSkew)
.withAuthenticationMaxAge(authenticationMaxAge)
.withLegacySameSiteCookie(useLegacySameSiteCookie)
@@ -330,26 +291,18 @@ private SignatureVerifier buildSignatureVerifier() {
}
@VisibleForTesting
- AuthAPI createAPIClient(String domain, String clientId, String clientSecret, HttpOptions httpOptions) {
- if (httpOptions != null) {
- return new AuthAPI(domain, clientId, clientSecret, httpOptions);
- }
- return new AuthAPI(domain, clientId, clientSecret);
- }
-
- @VisibleForTesting
- void setupTelemetry(AuthAPI client) {
- if (client == null)
- return;
- Telemetry telemetry = new Telemetry("auth0-java-mvc-common", obtainPackageVersion());
- client.setTelemetry(telemetry);
+ IdTokenVerifier.Options createIdTokenVerificationOptions(String issuer, String audience, SignatureVerifier signatureVerifier) {
+ return new IdTokenVerifier.Options(issuer, audience, signatureVerifier);
}
- @VisibleForTesting
- String obtainPackageVersion() {
- // Value if taken from jar's manifest file.
- // Call will return null on dev environment (outside of a jar)
- return getClass().getPackage().getImplementationVersion();
+ private String getIssuer(String domain) {
+ if (!domain.startsWith("http://") && !domain.startsWith("https://")) {
+ domain = "https://" + domain;
+ }
+ if (!domain.endsWith("/")) {
+ domain = domain + "/";
+ }
+ return domain;
}
}
@@ -360,7 +313,6 @@ String obtainPackageVersion() {
* @param enabled whether to enable the HTTP logger or not.
*/
public void setLoggingEnabled(boolean enabled) {
- // No longer requestProcessor.getClient()... (which was null)
requestProcessor.setLoggingEnabled(enabled);
}
@@ -372,35 +324,23 @@ public void doNotSendTelemetry() {
}
/**
- * Process a request to obtain a set of {@link Tokens} that represent successful
- * authentication or authorization.
+ * Process a request to obtain a set of {@link Tokens} that represent successful authentication or authorization.
*
- * This method should be called when processing the callback request to your
- * application. It will validate
- * authentication-related request parameters, handle performing a Code Exchange
- * request if using
- * the "code" response type, and verify the integrity of the ID token (if
- * present).
+ * This method should be called when processing the callback request to your application. It will validate
+ * authentication-related request parameters, handle performing a Code Exchange request if using
+ * the "code" response type, and verify the integrity of the ID token (if present).
*
- *
- * Important: When using this API, you must
- * also use
- * {@link AuthenticationController#buildAuthorizeUrl(HttpServletRequest, HttpServletResponse, String)}
- * when building the {@link AuthorizeUrl} that the user will be redirected to to
- * login. Failure to do so may result
- * in a broken login experience for the user.
- *
+ * Important: When using this API, you must also use {@link AuthenticationController#buildAuthorizeUrl(HttpServletRequest, HttpServletResponse, String)}
+ * when building the {@link AuthorizeUrl} that the user will be redirected to to login. Failure to do so may result
+ * in a broken login experience for the user.
*
- * @param request the received request to process.
+ * @param request the received request to process.
* @param response the received response to process.
* @return the Tokens obtained after the user authentication.
- * @throws InvalidRequestException if the error is result of making an
- * invalid authentication request.
- * @throws IdentityVerificationException if an error occurred while verifying
- * the request tokens.
+ * @throws InvalidRequestException if the error is result of making an invalid authentication request.
+ * @throws IdentityVerificationException if an error occurred while verifying the request tokens.
*/
- public Tokens handle(HttpServletRequest request, HttpServletResponse response)
- throws IdentityVerificationException {
+ public Tokens handle(HttpServletRequest request, HttpServletResponse response) throws IdentityVerificationException {
Validate.notNull(request, "request must not be null");
Validate.notNull(response, "response must not be null");
@@ -408,104 +348,18 @@ public Tokens handle(HttpServletRequest request, HttpServletResponse response)
}
/**
- * Process a request to obtain a set of {@link Tokens} that represent successful
- * authentication or authorization.
- *
- * This method should be called when processing the callback request to your
- * application. It will validate
- * authentication-related request parameters, handle performing a Code Exchange
- * request if using
- * the "code" response type, and verify the integrity of the ID token (if
- * present).
- *
- *
- * Important: When using this API, you must
- * also use the
- * {@link AuthenticationController#buildAuthorizeUrl(HttpServletRequest, String)}
- * when building the {@link AuthorizeUrl} that the user will be redirected to to
- * login. Failure to do so may result
- * in a broken login experience for the user.
- *
- *
- * @deprecated This method uses the {@link javax.servlet.http.HttpSession} for
- * auth-based data, and is incompatible
- * with clients that are using the "id_token" or "token"
- * responseType with browsers that enforce SameSite cookie
- * restrictions. This method will be removed in version 2.0.0. Use
- * {@link AuthenticationController#handle(HttpServletRequest, HttpServletResponse)}
- * instead.
- *
- * @param request the received request to process.
- * @return the Tokens obtained after the user authentication.
- * @throws InvalidRequestException if the error is result of making an
- * invalid authentication request.
- * @throws IdentityVerificationException if an error occurred while verifying
- * the request tokens.
- */
- @Deprecated
- public Tokens handle(HttpServletRequest request) throws IdentityVerificationException {
- Validate.notNull(request, "request must not be null");
-
- return requestProcessor.process(request, null);
- }
-
- /**
- * Pre builds an Auth0 Authorize Url with the given redirect URI using a random
- * state and a random nonce if applicable.
- *
- *
- * Important: When using this API, you must
- * also obtain the tokens using the
- * {@link AuthenticationController#handle(HttpServletRequest)} method. Failure
- * to do so may result in a broken login
- * experience for users.
- *
- *
- * @deprecated This method stores data in the
- * {@link javax.servlet.http.HttpSession}, and is incompatible with
- * clients
- * that are using the "id_token" or "token" responseType with
- * browsers that enforce SameSite cookie restrictions.
- * This method will be removed in version 2.0.0. Use
- * {@link AuthenticationController#buildAuthorizeUrl(HttpServletRequest, HttpServletResponse, String)}
- * instead.
- *
- * @param request the caller request. Used to keep the session context.
- * @param redirectUri the url to call back with the authentication result.
- * @return the authorize url builder to continue any further parameter
- * customization.
- */
- @Deprecated
- public AuthorizeUrl buildAuthorizeUrl(HttpServletRequest request, String redirectUri) {
- Validate.notNull(request, "request must not be null");
- Validate.notNull(redirectUri, "redirectUri must not be null");
-
- String state = StorageUtils.secureRandomString();
- String nonce = StorageUtils.secureRandomString();
-
- return requestProcessor.buildAuthorizeUrl(request, null, redirectUri, state, nonce);
- }
-
- /**
- * Pre builds an Auth0 Authorize Url with the given redirect URI using a random
- * state and a random nonce if applicable.
+ * Pre builds an Auth0 Authorize Url with the given redirect URI using a random state and a random nonce if applicable.
*
- *
- * Important: When using this API, you must
- * also obtain the tokens using the
- * {@link AuthenticationController#handle(HttpServletRequest, HttpServletResponse)}
- * method. Failure to do so will result in a broken login
- * experience for users.
- *
+ * Important: When using this API, you must also obtain the tokens using the
+ * {@link AuthenticationController#handle(HttpServletRequest, HttpServletResponse)} method. Failure to do so will result in a broken login
+ * experience for users.
*
* @param request the HTTP request
* @param response the HTTP response. Used to store auth-based cookies.
* @param redirectUri the url to call back with the authentication result.
- * @return the authorize url builder to continue any further parameter
- * customization.
+ * @return the authorize url builder to continue any further parameter customization.
*/
- public AuthorizeUrl buildAuthorizeUrl(HttpServletRequest request, HttpServletResponse response,
- String redirectUri) {
+ public AuthorizeUrl buildAuthorizeUrl(HttpServletRequest request, HttpServletResponse response, String redirectUri) {
Validate.notNull(request, "request must not be null");
Validate.notNull(response, "response must not be null");
Validate.notNull(redirectUri, "redirectUri must not be null");
diff --git a/src/main/java/com/auth0/AuthorizeUrl.java b/src/main/java/com/auth0/AuthorizeUrl.java
index 092b0fd..d0269ce 100644
--- a/src/main/java/com/auth0/AuthorizeUrl.java
+++ b/src/main/java/com/auth0/AuthorizeUrl.java
@@ -5,8 +5,7 @@
import com.auth0.exception.Auth0Exception;
import com.auth0.json.auth.PushedAuthorizationResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import java.util.*;
import static com.auth0.IdentityVerificationException.API_ERROR;
@@ -21,7 +20,6 @@ public class AuthorizeUrl {
private static final String SCOPE_OPENID = "openid";
private HttpServletResponse response;
- private HttpServletRequest request;
private final String responseType;
private boolean useLegacySameSiteCookie = true;
private boolean setSecureCookie = false;
@@ -39,19 +37,15 @@ public class AuthorizeUrl {
/**
* Creates a new instance that can be used to build an Auth0 Authorization URL.
*
- * Using this constructor with a non-null {@link HttpServletResponse} will store the state and nonce as
- * cookies when the {@link AuthorizeUrl#build()} method is called, with the appropriate SameSite attribute depending
- * on the responseType. State and nonce will also be stored in the {@link javax.servlet.http.HttpSession} as a fallback,
- * but this behavior will be removed in a future release, and only cookies will be used.
+ * Stores the state and nonce as cookies when the {@link AuthorizeUrl#build()} method is called,
+ * with the appropriate SameSite attribute depending on the responseType.
*
* @param client the Auth0 Authentication API client
- * @parem request the HTTP request. Used to store state and nonce as a fallback if cookies not set.
* @param response the response where the state and nonce will be stored as cookies
* @param redirectUri the url to redirect to after authentication
* @param responseType the response type to use
*/
- AuthorizeUrl(AuthAPI client, HttpServletRequest request, HttpServletResponse response, String redirectUri, String responseType) {
- this.request = request;
+ AuthorizeUrl(AuthAPI client, HttpServletResponse response, String redirectUri, String responseType) {
this.response = response;
this.responseType = responseType;
this.authAPI = client;
@@ -113,7 +107,7 @@ public AuthorizeUrl withSecureCookie(boolean secureCookie) {
/**
* Sets whether a fallback cookie should be used for clients that do not support "SameSite=None".
- * Only applicable when this instance is created with {@link AuthorizeUrl#AuthorizeUrl(AuthAPI, HttpServletRequest, HttpServletResponse, String, String)}.
+ * Only applicable when this instance is created with {@link AuthorizeUrl#AuthorizeUrl(AuthAPI, HttpServletResponse, String, String)}.
*
* @param useLegacySameSiteCookie whether or not to set fallback auth cookies for clients that do not support "SameSite=None"
* @return the builder instance
@@ -236,7 +230,7 @@ public String fromPushedAuthorizationRequest() throws InvalidRequestException {
storeTransient();
try {
- PushedAuthorizationResponse pushedAuthResponse = authAPI.pushedAuthorizationRequest(redirectUri, responseType, params).execute();
+ PushedAuthorizationResponse pushedAuthResponse = authAPI.pushedAuthorizationRequest(redirectUri, responseType, params).execute().getBody();
String requestUri = pushedAuthResponse.getRequestURI();
if (requestUri == null || requestUri.isEmpty()) {
throw new InvalidRequestException(API_ERROR, "The PAR request returned a missing or empty request_uri value");
@@ -255,24 +249,17 @@ private void storeTransient() {
throw new IllegalStateException("The AuthorizeUrl instance must not be reused.");
}
- if (response != null) {
- SameSite sameSiteValue = containsFormPost() ? SameSite.NONE : SameSite.LAX;
+ SameSite sameSiteValue = containsFormPost() ? SameSite.NONE : SameSite.LAX;
- TransientCookieStore.storeState(response, state, sameSiteValue, useLegacySameSiteCookie, setSecureCookie, cookiePath);
- TransientCookieStore.storeNonce(response, nonce, sameSiteValue, useLegacySameSiteCookie, setSecureCookie, cookiePath);
+ TransientCookieStore.storeState(response, state, sameSiteValue, useLegacySameSiteCookie, setSecureCookie, cookiePath);
+ TransientCookieStore.storeNonce(response, nonce, sameSiteValue, useLegacySameSiteCookie, setSecureCookie, cookiePath);
- // Store HMAC-signed origin domain with the same SameSite value as state/nonce
- if (originDomain != null && clientSecret != null) {
- TransientCookieStore.storeSignedOriginDomain(response, originDomain,
- sameSiteValue, cookiePath, setSecureCookie, clientSecret);
- }
+ // Store HMAC-signed origin domain with the same SameSite value as state/nonce
+ if (originDomain != null && clientSecret != null) {
+ TransientCookieStore.storeSignedOriginDomain(response, originDomain,
+ sameSiteValue, cookiePath, setSecureCookie, clientSecret);
}
- // Also store in Session just in case developer uses deprecated
- // AuthenticationController.handle(HttpServletRequest) API
- RandomStorage.setSessionState(request, state);
- RandomStorage.setSessionNonce(request, nonce);
-
used = true;
}
diff --git a/src/main/java/com/auth0/DomainProvider.java b/src/main/java/com/auth0/DomainProvider.java
index 081a3e7..edd8330 100644
--- a/src/main/java/com/auth0/DomainProvider.java
+++ b/src/main/java/com/auth0/DomainProvider.java
@@ -1,6 +1,6 @@
package com.auth0;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
interface DomainProvider {
String getDomain(HttpServletRequest request);
diff --git a/src/main/java/com/auth0/DomainResolver.java b/src/main/java/com/auth0/DomainResolver.java
index ea441e4..725e560 100644
--- a/src/main/java/com/auth0/DomainResolver.java
+++ b/src/main/java/com/auth0/DomainResolver.java
@@ -1,6 +1,6 @@
package com.auth0;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
public interface DomainResolver {
/**
diff --git a/src/main/java/com/auth0/InvalidRequestException.java b/src/main/java/com/auth0/InvalidRequestException.java
index 037338f..03690ca 100644
--- a/src/main/java/com/auth0/InvalidRequestException.java
+++ b/src/main/java/com/auth0/InvalidRequestException.java
@@ -18,15 +18,4 @@ public class InvalidRequestException extends IdentityVerificationException {
super(code, description != null ? description : DEFAULT_DESCRIPTION, cause);
}
- /**
- * Getter for the description of the error.
- *
- * @return the error description if available, null otherwise.
- * @deprecated use {@link #getMessage()}
- */
- @Deprecated
- public String getDescription() {
- return getMessage();
- }
-
}
diff --git a/src/main/java/com/auth0/RandomStorage.java b/src/main/java/com/auth0/RandomStorage.java
index 66659a0..e69de29 100644
--- a/src/main/java/com/auth0/RandomStorage.java
+++ b/src/main/java/com/auth0/RandomStorage.java
@@ -1,52 +0,0 @@
-package com.auth0;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-
-class RandomStorage extends SessionUtils {
-
- /**
- * Check's if the request {@link HttpSession} saved state is equal to the given state.
- * After the check, the value will be removed from the session.
- *
- * @param req the request
- * @param state the state value to compare against.
- * @return whether the state matches the expected one or not.
- */
- static boolean checkSessionState(HttpServletRequest req, String state) {
- String currentState = (String) remove(req, StorageUtils.STATE_KEY);
- return (currentState == null && state == null) || currentState != null && currentState.equals(state);
- }
-
- /**
- * Saves the given state in the request {@link HttpSession}.
- * If a state is already bound to the session, the value is replaced.
- *
- * @param req the request.
- * @param state the state value to set.
- */
- static void setSessionState(HttpServletRequest req, String state) {
- set(req, StorageUtils.STATE_KEY, state);
- }
-
- /**
- * Saves the given nonce in the request {@link HttpSession}.
- * If a nonce is already bound to the session, the value is replaced.
- *
- * @param req the request.
- * @param nonce the nonce value to set.
- */
- static void setSessionNonce(HttpServletRequest req, String nonce) {
- set(req, StorageUtils.NONCE_KEY, nonce);
- }
-
- /**
- * Removes the nonce present in the request {@link HttpSession} and then returns it.
- *
- * @param req the HTTP Servlet request.
- * @return the nonce value or null if it was not set.
- */
- static String removeSessionNonce(HttpServletRequest req) {
- return (String) remove(req, StorageUtils.NONCE_KEY);
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/auth0/RequestProcessor.java b/src/main/java/com/auth0/RequestProcessor.java
index cc58e7c..0919da6 100644
--- a/src/main/java/com/auth0/RequestProcessor.java
+++ b/src/main/java/com/auth0/RequestProcessor.java
@@ -1,14 +1,14 @@
package com.auth0;
-import com.auth0.client.HttpOptions;
+import com.auth0.client.LoggingOptions;
import com.auth0.client.auth.AuthAPI;
import com.auth0.exception.Auth0Exception;
import com.auth0.json.auth.TokenHolder;
-import com.auth0.net.Telemetry;
-import com.google.common.annotations.VisibleForTesting;
+import com.auth0.net.client.DefaultHttpClient;
+import org.apache.commons.lang3.Validate;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
@@ -16,8 +16,7 @@
/**
* Main class to handle the Authorize Redirect request.
- * It will try to parse the parameters looking for tokens or an authorization
- * code to perform a Code Exchange against the Auth0 servers.
+ * It will try to parse the parameters looking for tokens or an authorization code to perform a Code Exchange against the Auth0 servers.
*/
class RequestProcessor {
@@ -40,7 +39,6 @@ class RequestProcessor {
private final String responseType;
private final String clientId;
private final String clientSecret;
- private final HttpOptions httpOptions;
private SignatureVerifier signatureVerifier;
// Configuration values passed from Builder for creating per-request
@@ -51,7 +49,6 @@ class RequestProcessor {
private final String invitation;
final boolean useLegacySameSiteCookie;
- private AuthAPI client;
private final IdTokenVerifier tokenVerifier;
private final String cookiePath;
private boolean loggingEnabled = false;
@@ -62,7 +59,6 @@ static class Builder {
private final String responseType;
private final String clientId;
private final String clientSecret;
- private final HttpOptions httpOptions;
private final SignatureVerifier signatureVerifier;
private boolean useLegacySameSiteCookie = true;
@@ -76,13 +72,11 @@ public Builder(DomainProvider domainProvider,
String responseType,
String clientId,
String clientSecret,
- HttpOptions httpOptions,
SignatureVerifier signatureVerifier) {
this.domainProvider = domainProvider;
this.responseType = responseType;
this.clientId = clientId;
this.clientSecret = clientSecret;
- this.httpOptions = httpOptions;
this.signatureVerifier = signatureVerifier;
}
@@ -118,21 +112,19 @@ Builder withInvitation(String invitation) {
RequestProcessor build() {
- return new RequestProcessor(domainProvider, responseType, clientId, clientSecret, httpOptions,
+ return new RequestProcessor(domainProvider, responseType, clientId, clientSecret,
signatureVerifier, new IdTokenVerifier(),
useLegacySameSiteCookie, clockSkew, authenticationMaxAge, organization, invitation, cookiePath);
}
}
- private RequestProcessor(DomainProvider domainProvider, String responseType, String clientId, String clientSecret,
- HttpOptions httpOptions, SignatureVerifier signatureVerifier, IdTokenVerifier tokenVerifier,
+ private RequestProcessor(DomainProvider domainProvider, String responseType, String clientId, String clientSecret, SignatureVerifier signatureVerifier, IdTokenVerifier tokenVerifier,
boolean useLegacySameSiteCookie, Integer clockSkew, Integer authenticationMaxAge,
String organization, String invitation, String cookiePath) {
this.domainProvider = domainProvider;
this.responseType = responseType;
this.clientId = clientId;
this.clientSecret = clientSecret;
- this.httpOptions = httpOptions;
this.signatureVerifier = signatureVerifier;
this.tokenVerifier = tokenVerifier;
this.useLegacySameSiteCookie = useLegacySameSiteCookie;
@@ -153,67 +145,36 @@ void doNotSendTelemetry() {
this.telemetryDisabled = true;
}
- /**
- * Getter for the AuthAPI client instance.
- * Used to customize options such as Telemetry and Logging.
- *
- * @return the AuthAPI client.
- */
- AuthAPI getClient() {
- return client;
- }
-
AuthAPI createClientForDomain(String domain) {
- final AuthAPI client;
+ DefaultHttpClient.Builder httpBuilder = DefaultHttpClient.newBuilder()
+ .telemetryEnabled(!telemetryDisabled);
- if (httpOptions != null) {
- client = new AuthAPI(domain, clientId, clientSecret, httpOptions);
- } else {
- client = new AuthAPI(domain, clientId, clientSecret);
+ if (loggingEnabled) {
+ httpBuilder.withLogging(new LoggingOptions(LoggingOptions.LogLevel.BODY));
}
- // Apply deferred settings
- client.setLoggingEnabled(loggingEnabled);
- if (telemetryDisabled) {
- client.doNotSendTelemetry();
- } else {
- setupTelemetry(client);
- }
-
- return client;
- }
-
- void setupTelemetry(AuthAPI client) {
- Telemetry telemetry = new Telemetry("auth0-java-mvc-common", obtainPackageVersion());
- client.setTelemetry(telemetry);
- }
-
- @VisibleForTesting
- String obtainPackageVersion() {
- return getClass().getPackage().getImplementationVersion();
+ return AuthAPI.newBuilder(domain, clientId, clientSecret)
+ .withHttpClient(httpBuilder.build())
+ .build();
}
/**
- * Pre builds an Auth0 Authorize Url with the given redirect URI, state and
- * nonce parameters.
+ * Pre builds an Auth0 Authorize Url with the given redirect URI, state and nonce parameters.
*
- * @param request the request, used to store state and nonce in the Session
- * @param response the response, used to set state and nonce as cookies. If
- * null, session will be used instead.
+ * @param request the HTTP request.
+ * @param response the HTTP response, used to set state and nonce as cookies.
* @param redirectUri the url to call with the authentication result.
* @param state a valid state value.
- * @param nonce the nonce value that will be used if the response type
- * contains 'id_token'. Can be null.
- * @return the authorize url builder to continue any further parameter
- * customization.
+ * @param nonce the nonce value that will be used if the response type contains 'id_token'. Can be null.
+ * @return the authorize url builder to continue any further parameter customization.
*/
AuthorizeUrl buildAuthorizeUrl(HttpServletRequest request, HttpServletResponse response, String redirectUri,
- String state, String nonce) {
+ String state, String nonce) {
String originDomain = domainProvider.getDomain(request);
AuthAPI client = createClientForDomain(originDomain);
- AuthorizeUrl creator = new AuthorizeUrl(client, request, response, redirectUri, responseType)
+ AuthorizeUrl creator = new AuthorizeUrl(client, response, redirectUri, responseType)
.withState(state);
if (this.organization != null) {
@@ -226,12 +187,8 @@ AuthorizeUrl buildAuthorizeUrl(HttpServletRequest request, HttpServletResponse r
creator.withCookiePath(this.cookiePath);
}
- // null response means state and nonce will be stored in session, so legacy
- // cookie flag does not apply and origin domain cookie cannot be set
- if (response != null) {
- creator.withLegacySameSiteCookie(useLegacySameSiteCookie);
- creator.withOriginDomain(originDomain, clientSecret);
- }
+ creator.withLegacySameSiteCookie(useLegacySameSiteCookie);
+ creator.withOriginDomain(originDomain, clientSecret);
return getAuthorizeUrl(nonce, creator);
}
@@ -240,14 +197,12 @@ AuthorizeUrl buildAuthorizeUrl(HttpServletRequest request, HttpServletResponse r
* Entrypoint for HTTP request
*
* 1). Responsible for validating the request.
- * 2). Exchanging the authorization code received with this HTTP request for
- * Auth0 tokens.
+ * 2). Exchanging the authorization code received with this HTTP request for Auth0 tokens.
* 3). Validating the ID Token.
* 4). Clearing the stored state, nonce and max_age values.
* 5). Handling success and any failure outcomes.
*
- * @throws IdentityVerificationException if an error occurred while processing
- * the request
+ * @throws IdentityVerificationException if an error occurred while processing the request
*/
Tokens process(HttpServletRequest request, HttpServletResponse response) throws IdentityVerificationException {
assertNoError(request);
@@ -256,9 +211,9 @@ Tokens process(HttpServletRequest request, HttpServletResponse response) throws
// Extract origin_domain from the HMAC-signed transaction state cookie.
// If the cookie was tampered with, getSignedOriginDomain returns null.
String originDomain = null;
- if (response != null) {
- originDomain = TransientCookieStore.getSignedOriginDomain(request, response, clientSecret);
- }
+
+ originDomain = TransientCookieStore.getSignedOriginDomain(request, response, clientSecret);
+
// Fallback for session-based (deprecated) flow or if cookie was not set
if (originDomain == null) {
@@ -288,36 +243,19 @@ static boolean requiresFormPostResponseMode(List responseType) {
/**
* Obtains code request tokens (if using Code flow) and validates the ID token.
- *
- * @param request the HTTP request
- * @param response the HTTP response
+ * @param request the HTTP request
* @param frontChannelTokens the tokens obtained from the front channel
- * @param responseTypeList the reponse types
- * @param originDomain the domain for this specific request
- * @param originIssuer the issuer for this specific request
- * @return a Tokens object that wraps the values obtained from the front-channel
- * and/or the code request response.
+ * @param responseTypeList the reponse types
+ * @return a Tokens object that wraps the values obtained from the front-channel and/or the code request response.
* @throws IdentityVerificationException
*/
- private Tokens getVerifiedTokens(HttpServletRequest request, HttpServletResponse response,
- Tokens frontChannelTokens,
- List responseTypeList, String originDomain, String originIssuer)
+ private Tokens getVerifiedTokens(HttpServletRequest request, HttpServletResponse response, Tokens frontChannelTokens, List responseTypeList, String originDomain, String originIssuer)
throws IdentityVerificationException {
String authorizationCode = request.getParameter(KEY_CODE);
Tokens codeExchangeTokens = null;
- // Get nonce for this specific request
- String nonce;
- if (response != null) {
- nonce = TransientCookieStore.getNonce(request, response);
- // Fallback to session if cookie was not set (deprecated API path)
- if (nonce == null) {
- nonce = RandomStorage.removeSessionNonce(request);
- }
- } else {
- nonce = RandomStorage.removeSessionNonce(request);
- }
+ String nonce = TransientCookieStore.getNonce(request, response);
IdTokenVerifier.Options requestVerifyOptions = createRequestVerifyOptions(originIssuer, nonce);
@@ -341,11 +279,9 @@ private Tokens getVerifiedTokens(HttpServletRequest request, HttpServletResponse
}
}
} catch (TokenValidationException e) {
- throw new IdentityVerificationException(JWT_VERIFICATION_ERROR,
- "An error occurred while trying to verify the ID Token.", e);
+ throw new IdentityVerificationException(JWT_VERIFICATION_ERROR, "An error occurred while trying to verify the ID Token.", e);
} catch (Auth0Exception e) {
- throw new IdentityVerificationException(API_ERROR,
- "An error occurred while exchanging the authorization code.", e);
+ throw new IdentityVerificationException(API_ERROR, "An error occurred while exchanging the authorization code.", e);
}
// Keep the front-channel ID Token and the code-exchange Access Token.
return mergeTokens(frontChannelTokens, codeExchangeTokens);
@@ -394,20 +330,16 @@ private AuthorizeUrl getAuthorizeUrl(String nonce, AuthorizeUrl creator) {
}
/**
- * Extract the tokens from the request parameters, present when using the
- * Implicit or Hybrid Grant.
+ * Extract the tokens from the request parameters, present when using the Implicit or Hybrid Grant.
*
- * @param request the request
+ * @param request the request
* @param originDomain the domain that issued these tokens
* @param originIssuer the issuer that issued these tokens
- * @return a new instance of Tokens wrapping the values present in the request
- * parameters.
+ * @return a new instance of Tokens wrapping the values present in the request parameters.
*/
private Tokens getFrontChannelTokens(HttpServletRequest request, String originDomain, String originIssuer) {
- Long expiresIn = request.getParameter(KEY_EXPIRES_IN) == null ? null
- : Long.parseLong(request.getParameter(KEY_EXPIRES_IN));
- return new Tokens(request.getParameter(KEY_ACCESS_TOKEN), request.getParameter(KEY_ID_TOKEN), null,
- request.getParameter(KEY_TOKEN_TYPE), expiresIn, originDomain, originIssuer);
+ Long expiresIn = request.getParameter(KEY_EXPIRES_IN) == null ? null : Long.parseLong(request.getParameter(KEY_EXPIRES_IN));
+ return new Tokens(request.getParameter(KEY_ACCESS_TOKEN), request.getParameter(KEY_ID_TOKEN), null, request.getParameter(KEY_TOKEN_TYPE), expiresIn, originDomain, originIssuer);
}
/**
@@ -425,62 +357,28 @@ private void assertNoError(HttpServletRequest request) throws InvalidRequestExce
}
/**
- * Checks whether the state received in the request parameters is the same as
- * the one in the state cookie or session
+ * Checks whether the state received in the request parameters is the same as the one in the state cookie
* for this request.
*
- * @param request the request
- * @throws InvalidRequestException if the request contains a different state
- * from the expected one
+ * @param request the request
+ * @param response the response, used to remove the state cookie
+ * @throws InvalidRequestException if the request contains a different state from the expected one
*/
- private void assertValidState(HttpServletRequest request, HttpServletResponse response)
- throws InvalidRequestException {
- // TODO in v2:
- // - only store state/nonce in cookies, remove session storage
- // - create specific exception classes for various state validation failures
- // (missing from auth response, missing
- // state cookie, mismatch)
-
+ private void assertValidState(HttpServletRequest request, HttpServletResponse response) throws InvalidRequestException {
String stateFromRequest = request.getParameter(KEY_STATE);
if (stateFromRequest == null) {
- throw new InvalidRequestException(INVALID_STATE_ERROR,
- "The received state doesn't match the expected one. No state parameter was found on the authorization response.");
- }
-
- // If response is null, check the Session.
- // This can happen when the deprecated handle method that only takes the request
- // parameter is called
- if (response == null) {
- checkSessionState(request, stateFromRequest);
- return;
+ throw new InvalidRequestException(INVALID_STATE_ERROR, "The received state doesn't match the expected one. No state parameter was found on the authorization response.");
}
String cookieState = TransientCookieStore.getState(request, response);
- // Just in case state was stored in Session by building auth URL with deprecated
- // method, but then called the
- // supported handle method with the request and response
if (cookieState == null) {
- if (SessionUtils.get(request, StorageUtils.STATE_KEY) == null) {
- throw new InvalidRequestException(INVALID_STATE_ERROR,
- "The received state doesn't match the expected one. No state cookie or state session attribute found. Check that you are using non-deprecated methods and that cookies are not being removed on the server.");
- }
- checkSessionState(request, stateFromRequest);
- return;
+ throw new InvalidRequestException(INVALID_STATE_ERROR, "The received state doesn't match the expected one. No state cookie found. Check that cookies are not being removed on the server.");
}
if (!cookieState.equals(stateFromRequest)) {
- throw new InvalidRequestException(INVALID_STATE_ERROR,
- "The received state doesn't match the expected one.");
- }
- }
-
- private void checkSessionState(HttpServletRequest request, String stateFromRequest) throws InvalidRequestException {
- boolean valid = RandomStorage.checkSessionState(request, stateFromRequest);
- if (!valid) {
- throw new InvalidRequestException(INVALID_STATE_ERROR,
- "The received state doesn't match the expected one.");
+ throw new InvalidRequestException(INVALID_STATE_ERROR, "The received state doesn't match the expected one.");
}
}
@@ -489,26 +387,23 @@ private void checkSessionState(HttpServletRequest request, String stateFromReque
*
* @param authorizationCode the code received on the login response.
* @param redirectUri the redirect uri used on login request.
- * @param originDomain the domain that issued these tokens.
* @return a new instance of {@link Tokens} with the received credentials.
* @throws Auth0Exception if the request to the Auth0 server failed.
* @see AuthAPI#exchangeCode(String, String)
*/
- private Tokens exchangeCodeForTokens(String authorizationCode, String redirectUri, String originDomain)
- throws Auth0Exception {
+ private Tokens exchangeCodeForTokens(String authorizationCode, String redirectUri, String originDomain) throws Auth0Exception {
AuthAPI client = createClientForDomain(originDomain);
TokenHolder holder = client
.exchangeCode(authorizationCode, redirectUri)
- .execute();
+ .execute()
+ .getBody();
String originIssuer = constructIssuer(originDomain);
- return new Tokens(holder.getAccessToken(), holder.getIdToken(), holder.getRefreshToken(), holder.getTokenType(),
- holder.getExpiresIn(), originDomain, originIssuer);
+ return new Tokens(holder.getAccessToken(), holder.getIdToken(), holder.getRefreshToken(), holder.getTokenType(), holder.getExpiresIn(), originDomain, originIssuer);
}
/**
* Used to keep the best version of each token.
- * It will prioritize the ID Token received in the front-channel, and the Access
- * Token received in the code exchange request.
+ * It will prioritize the ID Token received in the front-channel, and the Access Token received in the code exchange request.
*
* @param frontChannelTokens the front-channel obtained tokens.
* @param codeExchangeTokens the code-exchange obtained tokens.
@@ -535,8 +430,7 @@ private Tokens mergeTokens(Tokens frontChannelTokens, Tokens codeExchangeTokens)
}
// Prefer ID token from the front-channel
- String idToken = frontChannelTokens.getIdToken() != null ? frontChannelTokens.getIdToken()
- : codeExchangeTokens.getIdToken();
+ String idToken = frontChannelTokens.getIdToken() != null ? frontChannelTokens.getIdToken() : codeExchangeTokens.getIdToken();
// Refresh token only available from the code exchange
String refreshToken = codeExchangeTokens.getRefreshToken();
diff --git a/src/main/java/com/auth0/ResolverDomainProvider.java b/src/main/java/com/auth0/ResolverDomainProvider.java
index e3ed73e..86dd9eb 100644
--- a/src/main/java/com/auth0/ResolverDomainProvider.java
+++ b/src/main/java/com/auth0/ResolverDomainProvider.java
@@ -1,6 +1,6 @@
package com.auth0;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
class ResolverDomainProvider implements DomainProvider {
private final DomainResolver resolver;
diff --git a/src/main/java/com/auth0/SessionUtils.java b/src/main/java/com/auth0/SessionUtils.java
index a6906dc..e69de29 100644
--- a/src/main/java/com/auth0/SessionUtils.java
+++ b/src/main/java/com/auth0/SessionUtils.java
@@ -1,64 +0,0 @@
-package com.auth0;
-
-import org.apache.commons.lang3.Validate;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-
-/**
- * Helper class to handle easy session key-value storage.
- */
-@SuppressWarnings({"WeakerAccess", "unused"})
-public abstract class SessionUtils {
-
- /**
- * Extracts the HttpSession from the given request.
- *
- * @param req a valid request to get the session from
- * @return the session of the request
- */
- protected static HttpSession getSession(HttpServletRequest req) {
- return req.getSession(true);
- }
-
- /**
- * Set's the attribute value to the request session.
- *
- * @param req a valid request to get the session from
- * @param name the name of the attribute
- * @param value the value to set
- */
- public static void set(HttpServletRequest req, String name, Object value) {
- Validate.notNull(req);
- Validate.notNull(name);
- getSession(req).setAttribute(name, value);
- }
-
- /**
- * Get the attribute with the given name from the request session.
- *
- * @param req a valid request to get the session from
- * @param name the name of the attribute
- * @return the attribute stored in the session or null if it doesn't exists
- */
- public static Object get(HttpServletRequest req, String name) {
- Validate.notNull(req);
- Validate.notNull(name);
- return getSession(req).getAttribute(name);
- }
-
- /**
- * Same as {@link #get(HttpServletRequest, String)} but it also removes the value from the request session.
- *
- * @param req a valid request to get the session from
- * @param name the name of the attribute
- * @return the attribute stored in the session or null if it doesn't exists
- */
- public static Object remove(HttpServletRequest req, String name) {
- Validate.notNull(req);
- Validate.notNull(name);
- Object value = get(req, name);
- getSession(req).removeAttribute(name);
- return value;
- }
-}
diff --git a/src/main/java/com/auth0/StaticDomainProvider.java b/src/main/java/com/auth0/StaticDomainProvider.java
index c0421ca..1f806a3 100644
--- a/src/main/java/com/auth0/StaticDomainProvider.java
+++ b/src/main/java/com/auth0/StaticDomainProvider.java
@@ -1,6 +1,6 @@
package com.auth0;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
class StaticDomainProvider implements DomainProvider {
private final String domain;
diff --git a/src/main/java/com/auth0/TransientCookieStore.java b/src/main/java/com/auth0/TransientCookieStore.java
index 8ede8f3..7b45ab9 100644
--- a/src/main/java/com/auth0/TransientCookieStore.java
+++ b/src/main/java/com/auth0/TransientCookieStore.java
@@ -2,9 +2,9 @@
import org.apache.commons.lang3.Validate;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
diff --git a/src/test/java/com/auth0/AuthenticationControllerTest.java b/src/test/java/com/auth0/AuthenticationControllerTest.java
index 32239d8..4fc78ea 100644
--- a/src/test/java/com/auth0/AuthenticationControllerTest.java
+++ b/src/test/java/com/auth0/AuthenticationControllerTest.java
@@ -1,6 +1,5 @@
package com.auth0;
-import com.auth0.client.HttpOptions;
import com.auth0.jwk.JwkProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -9,8 +8,8 @@
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
@@ -30,8 +29,6 @@ public class AuthenticationControllerTest {
@Mock
private JwkProvider mockJwkProvider;
@Mock
- private HttpOptions mockHttpOptions;
- @Mock
private DomainResolver mockDomainResolver;
@Mock
private Tokens mockTokens;
@@ -41,7 +38,7 @@ public class AuthenticationControllerTest {
@BeforeEach
public void setUp() {
- MockitoAnnotations.initMocks(this);
+ MockitoAnnotations.openMocks(this);
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
@@ -90,7 +87,6 @@ public void shouldConfigureBuilderWithAllOptions() {
.withLegacySameSiteCookie(false)
.withOrganization("org_123")
.withInvitation("inv_456")
- .withHttpOptions(mockHttpOptions)
.withCookiePath("/custom")
.build();
@@ -139,7 +135,6 @@ public void shouldValidateNullParameters() {
assertThrows(NullPointerException.class, () -> builder.withAuthenticationMaxAge(null));
assertThrows(NullPointerException.class, () -> builder.withOrganization(null));
assertThrows(NullPointerException.class, () -> builder.withInvitation(null));
- assertThrows(NullPointerException.class, () -> builder.withHttpOptions(null));
assertThrows(NullPointerException.class, () -> builder.withCookiePath(null));
}
@@ -326,15 +321,6 @@ public void shouldBuildWithDomainResolver() {
assertThat(controller, is(notNullValue()));
}
- @Test
- public void shouldBuildWithCustomHttpOptions() {
- AuthenticationController controller = AuthenticationController.newBuilder(DOMAIN, CLIENT_ID, CLIENT_SECRET)
- .withHttpOptions(mockHttpOptions)
- .build();
-
- assertThat(controller, is(notNullValue()));
- }
-
@Test
public void shouldBuildWithOrganizationAndInvitation() {
AuthenticationController controller = AuthenticationController.newBuilder(DOMAIN, CLIENT_ID, CLIENT_SECRET)
diff --git a/src/test/java/com/auth0/AuthorizeUrlTest.java b/src/test/java/com/auth0/AuthorizeUrlTest.java
index 5818265..d8058ec 100644
--- a/src/test/java/com/auth0/AuthorizeUrlTest.java
+++ b/src/test/java/com/auth0/AuthorizeUrlTest.java
@@ -1,23 +1,21 @@
package com.auth0;
-import com.auth0.client.HttpOptions;
import com.auth0.client.auth.AuthAPI;
import com.auth0.exception.Auth0Exception;
import com.auth0.json.auth.PushedAuthorizationResponse;
import com.auth0.net.Request;
+import com.auth0.net.Response;
import okhttp3.HttpUrl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import java.util.Collection;
-import java.util.Map;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.matchesPattern;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.*;
@@ -28,18 +26,16 @@ public class AuthorizeUrlTest {
private AuthAPI client;
private HttpServletResponse response;
- private HttpServletRequest request;
@BeforeEach
public void setUp() {
- client = new AuthAPI("domain.auth0.com", "clientId", "clientSecret");
- request = new MockHttpServletRequest();
+ client = AuthAPI.newBuilder("domain.auth0.com", "clientId", "clientSecret").build();
response = new MockHttpServletResponse();
}
@Test
public void shouldBuildValidStringUrl() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.build();
assertThat(url, is(notNullValue()));
assertThat(HttpUrl.parse(url), is(notNullValue()));
@@ -47,28 +43,28 @@ public void shouldBuildValidStringUrl() {
@Test
public void shouldSetDefaultScope() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.build();
assertThat(HttpUrl.parse(url).queryParameter("scope"), is("openid"));
}
@Test
public void shouldSetResponseType() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.build();
assertThat(HttpUrl.parse(url).queryParameter("response_type"), is("id_token token"));
}
@Test
public void shouldSetRedirectUrl() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.build();
assertThat(HttpUrl.parse(url).queryParameter("redirect_uri"), is("https://redirect.to/me"));
}
@Test
public void shouldSetConnection() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withConnection("facebook")
.build();
assertThat(HttpUrl.parse(url).queryParameter("connection"), is("facebook"));
@@ -76,7 +72,7 @@ public void shouldSetConnection() {
@Test
public void shouldSetAudience() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withAudience("https://api.auth0.com/")
.build();
assertThat(HttpUrl.parse(url).queryParameter("audience"), is("https://api.auth0.com/"));
@@ -84,20 +80,20 @@ public void shouldSetAudience() {
@Test
public void shouldSetNonceSameSiteAndLegacyCookieByDefault() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withNonce("asdfghjkl")
.build();
assertThat(HttpUrl.parse(url).queryParameter("nonce"), is("asdfghjkl"));
Collection headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(2));
- assertThat(headers, hasItem("com.auth0.nonce=asdfghjkl; HttpOnly; Max-Age=600; SameSite=None; Secure"));
- assertThat(headers, hasItem("_com.auth0.nonce=asdfghjkl; HttpOnly; Max-Age=600"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.nonce=asdfghjkl; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
+ assertThat(headers, hasItem(matchesPattern("_com\\.auth0\\.nonce=asdfghjkl; Max-Age=600; Expires=.*?; HttpOnly")));
}
@Test
public void shouldSetNonceSameSiteAndNotLegacyCookieWhenConfigured() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withNonce("asdfghjkl")
.withLegacySameSiteCookie(false)
.build();
@@ -105,25 +101,25 @@ public void shouldSetNonceSameSiteAndNotLegacyCookieWhenConfigured() {
Collection headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(1));
- assertThat(headers, hasItem("com.auth0.nonce=asdfghjkl; HttpOnly; Max-Age=600; SameSite=None; Secure"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.nonce=asdfghjkl; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
}
@Test
public void shouldSetStateSameSiteAndLegacyCookieByDefault() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withState("asdfghjkl")
.build();
assertThat(HttpUrl.parse(url).queryParameter("state"), is("asdfghjkl"));
Collection headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(2));
- assertThat(headers, hasItem("com.auth0.state=asdfghjkl; HttpOnly; Max-Age=600; SameSite=None; Secure"));
- assertThat(headers, hasItem("_com.auth0.state=asdfghjkl; HttpOnly; Max-Age=600"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.state=asdfghjkl; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
+ assertThat(headers, hasItem(matchesPattern("_com\\.auth0\\.state=asdfghjkl; Max-Age=600; Expires=.*?; HttpOnly")));
}
@Test
public void shouldSetStateSameSiteAndNotLegacyCookieWhenConfigured() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withState("asdfghjkl")
.withLegacySameSiteCookie(false)
.build();
@@ -131,12 +127,12 @@ public void shouldSetStateSameSiteAndNotLegacyCookieWhenConfigured() {
Collection headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(1));
- assertThat(headers, hasItem("com.auth0.state=asdfghjkl; HttpOnly; Max-Age=600; SameSite=None; Secure"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.state=asdfghjkl; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
}
@Test
public void shouldSetSecureCookieWhenConfiguredTrue() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "code")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "code")
.withState("asdfghjkl")
.withSecureCookie(true)
.build();
@@ -144,12 +140,12 @@ public void shouldSetSecureCookieWhenConfiguredTrue() {
Collection headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(1));
- assertThat(headers, hasItem("com.auth0.state=asdfghjkl; HttpOnly; Max-Age=600; SameSite=Lax; Secure"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.state=asdfghjkl; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=Lax")));
}
@Test
public void shouldSetSecureCookieWhenConfiguredFalseAndSameSiteNone() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token")
.withState("asdfghjkl")
.withSecureCookie(false)
.build();
@@ -157,24 +153,13 @@ public void shouldSetSecureCookieWhenConfiguredFalseAndSameSiteNone() {
Collection headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(2));
- assertThat(headers, hasItem("com.auth0.state=asdfghjkl; HttpOnly; Max-Age=600; SameSite=None; Secure"));
- assertThat(headers, hasItem("_com.auth0.state=asdfghjkl; HttpOnly; Max-Age=600"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.state=asdfghjkl; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
+ assertThat(headers, hasItem(matchesPattern("_com\\.auth0\\.state=asdfghjkl; Max-Age=600; Expires=.*?; HttpOnly")));
}
@Test
public void shouldSetNoCookiesWhenNonceAndStateNotSet() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
- .build();
- assertThat(HttpUrl.parse(url).queryParameter("state"), nullValue());
- assertThat(HttpUrl.parse(url).queryParameter("nonce"), nullValue());
-
- Collection headers = response.getHeaders("Set-Cookie");
- assertThat(headers.size(), is(0));
- }
-
- @Test
- public void shouldSetNoSessionValuesWhenNonceAndStateNotSet() {
- String url = new AuthorizeUrl(client, request, null, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.build();
assertThat(HttpUrl.parse(url).queryParameter("state"), nullValue());
assertThat(HttpUrl.parse(url).queryParameter("nonce"), nullValue());
@@ -185,7 +170,7 @@ public void shouldSetNoSessionValuesWhenNonceAndStateNotSet() {
@Test
public void shouldSetScope() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withScope("openid profile email")
.build();
assertThat(HttpUrl.parse(url).queryParameter("scope"), is("openid profile email"));
@@ -193,7 +178,7 @@ public void shouldSetScope() {
@Test
public void shouldSetCustomParameterScope() {
- String url = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ String url = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withParameter("custom", "value")
.build();
assertThat(HttpUrl.parse(url).queryParameter("custom"), is("value"));
@@ -201,7 +186,7 @@ public void shouldSetCustomParameterScope() {
@Test
public void shouldThrowWhenReusingTheInstance() {
- AuthorizeUrl builder = new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token");
+ AuthorizeUrl builder = new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token");
String firstCall = builder.build();
assertThat(firstCall, is(notNullValue()));
IllegalStateException e = assertThrows(IllegalStateException.class, builder::build);
@@ -212,7 +197,7 @@ public void shouldThrowWhenReusingTheInstance() {
public void shouldThrowWhenChangingTheRedirectURI() {
IllegalArgumentException e = assertThrows(
IllegalArgumentException.class,
- () -> new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ () -> new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withParameter("redirect_uri", "new_value"));
assertEquals("Redirect URI cannot be changed once set.", e.getMessage());
}
@@ -221,7 +206,7 @@ public void shouldThrowWhenChangingTheRedirectURI() {
public void shouldThrowWhenChangingTheResponseType() {
IllegalArgumentException e = assertThrows(
IllegalArgumentException.class,
- () -> new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ () -> new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withParameter("response_type", "new_value"));
assertEquals("Response type cannot be changed once set.", e.getMessage());
}
@@ -230,7 +215,7 @@ public void shouldThrowWhenChangingTheResponseType() {
public void shouldThrowWhenChangingTheStateUsingCustomParameterSetter() {
IllegalArgumentException e = assertThrows(
IllegalArgumentException.class,
- () -> new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ () -> new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withParameter("state", "new_value"));
assertEquals("Please, use the dedicated methods for setting the 'nonce' and 'state' parameters.", e.getMessage());
}
@@ -239,20 +224,26 @@ public void shouldThrowWhenChangingTheStateUsingCustomParameterSetter() {
public void shouldThrowWhenChangingTheNonceUsingCustomParameterSetter() {
IllegalArgumentException e = assertThrows(
IllegalArgumentException.class,
- () -> new AuthorizeUrl(client, request, response, "https://redirect.to/me", "id_token token")
+ () -> new AuthorizeUrl(client, response, "https://redirect.to/me", "id_token token")
.withParameter("nonce", "new_value"));
assertEquals("Please, use the dedicated methods for setting the 'nonce' and 'state' parameters.", e.getMessage());
}
@Test
public void shouldGetAuthorizeUrlFromPAR() throws Exception {
- AuthAPIStub authAPIStub = new AuthAPIStub("https://domain.com", "clientId", "clientSecret");
+ AuthAPI authAPIMock = mock(AuthAPI.class);
Request requestMock = mock(Request.class);
- when(requestMock.execute()).thenReturn(new PushedAuthorizationResponse("urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2", 90));
+ Response pushedAuthorizationResponseResponse = mock(Response.class);
+ when(requestMock.execute()).thenReturn(pushedAuthorizationResponseResponse);
+ when(requestMock.execute().getBody()).thenReturn(new PushedAuthorizationResponse("urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2", 90));
- authAPIStub.pushedAuthorizationResponseRequest = requestMock;
- String url = new AuthorizeUrl(authAPIStub, request, response, "https://domain.com/callback", "code")
+ when(authAPIMock.pushedAuthorizationRequest(eq("https://domain.com/callback"), eq("code"), anyMap()))
+ .thenReturn(requestMock);
+ when(authAPIMock.authorizeUrlWithPAR("urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2"))
+ .thenReturn("https://domain.com/authorize?client_id=clientId&request_uri=urn%3Aexample%3Abwc4JK-ESC0w8acc191e-Y1LTC2");
+
+ String url = new AuthorizeUrl(authAPIMock, response, "https://domain.com/callback", "code")
.fromPushedAuthorizationRequest();
assertThat(url, is("https://domain.com/authorize?client_id=clientId&request_uri=urn%3Aexample%3Abwc4JK-ESC0w8acc191e-Y1LTC2"));
@@ -260,14 +251,17 @@ public void shouldGetAuthorizeUrlFromPAR() throws Exception {
@Test
public void fromPushedAuthorizationRequestThrowsWhenRequestUriIsNull() throws Exception {
- AuthAPIStub authAPIStub = new AuthAPIStub("https://domain.com", "clientId", "clientSecret");
+ AuthAPI authAPIMock = mock(AuthAPI.class);
Request requestMock = mock(Request.class);
- when(requestMock.execute()).thenReturn(new PushedAuthorizationResponse(null, 90));
+ Response pushedAuthorizationResponseResponse = mock(Response.class);
+ when(requestMock.execute()).thenReturn(pushedAuthorizationResponseResponse);
+ when(requestMock.execute().getBody()).thenReturn(new PushedAuthorizationResponse(null, 90));
- authAPIStub.pushedAuthorizationResponseRequest = requestMock;
+ when(authAPIMock.pushedAuthorizationRequest(eq("https://domain.com/callback"), eq("code"), anyMap()))
+ .thenReturn(requestMock);
InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> {
- new AuthorizeUrl(authAPIStub, request, response, "https://domain.com/callback", "code")
+ new AuthorizeUrl(authAPIMock, response, "https://domain.com/callback", "code")
.fromPushedAuthorizationRequest();
});
@@ -276,14 +270,17 @@ public void fromPushedAuthorizationRequestThrowsWhenRequestUriIsNull() throws Ex
@Test
public void fromPushedAuthorizationRequestThrowsWhenRequestUriIsEmpty() throws Exception {
- AuthAPIStub authAPIStub = new AuthAPIStub("https://domain.com", "clientId", "clientSecret");
+ AuthAPI authAPIMock = mock(AuthAPI.class);
Request requestMock = mock(Request.class);
- when(requestMock.execute()).thenReturn(new PushedAuthorizationResponse("urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2", null));
+ Response pushedAuthorizationResponseResponse = mock(Response.class);
+ when(requestMock.execute()).thenReturn(pushedAuthorizationResponseResponse);
+ when(requestMock.execute().getBody()).thenReturn(new PushedAuthorizationResponse("urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2", null));
- authAPIStub.pushedAuthorizationResponseRequest = requestMock;
+ when(authAPIMock.pushedAuthorizationRequest(eq("https://domain.com/callback"), eq("code"), anyMap()))
+ .thenReturn(requestMock);
InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> {
- new AuthorizeUrl(authAPIStub, request, response, "https://domain.com/callback", "code")
+ new AuthorizeUrl(authAPIMock, response, "https://domain.com/callback", "code")
.fromPushedAuthorizationRequest();
});
@@ -292,14 +289,17 @@ public void fromPushedAuthorizationRequestThrowsWhenRequestUriIsEmpty() throws E
@Test
public void fromPushedAuthorizationRequestThrowsWhenExpiresInIsNull() throws Exception {
- AuthAPIStub authAPIStub = new AuthAPIStub("https://domain.com", "clientId", "clientSecret");
+ AuthAPI authAPIMock = mock(AuthAPI.class);
Request requestMock = mock(Request.class);
- when(requestMock.execute()).thenReturn(new PushedAuthorizationResponse(null, 90));
+ Response pushedAuthorizationResponseResponse = mock(Response.class);
+ when(requestMock.execute()).thenReturn(pushedAuthorizationResponseResponse);
+ when(requestMock.execute().getBody()).thenReturn(new PushedAuthorizationResponse(null, 90));
- authAPIStub.pushedAuthorizationResponseRequest = requestMock;
+ when(authAPIMock.pushedAuthorizationRequest(eq("https://domain.com/callback"), eq("code"), anyMap()))
+ .thenReturn(requestMock);
InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> {
- new AuthorizeUrl(authAPIStub, request, response, "https://domain.com/callback", "code")
+ new AuthorizeUrl(authAPIMock, response, "https://domain.com/callback", "code")
.fromPushedAuthorizationRequest();
});
@@ -317,7 +317,7 @@ public void fromPushedAuthorizationRequestThrowsWhenRequestThrows() throws Excep
.thenReturn(requestMock);
InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> {
- new AuthorizeUrl(authAPIMock, request, response, "https://domain.com/callback", "code")
+ new AuthorizeUrl(authAPIMock, response, "https://domain.com/callback", "code")
.fromPushedAuthorizationRequest();
});
@@ -325,21 +325,4 @@ public void fromPushedAuthorizationRequestThrowsWhenRequestThrows() throws Excep
assertThat(exception.getCause(), instanceOf(Auth0Exception.class));
}
- static class AuthAPIStub extends AuthAPI {
-
- Request pushedAuthorizationResponseRequest;
-
- public AuthAPIStub(String domain, String clientId, String clientSecret, HttpOptions options) {
- super(domain, clientId, clientSecret, options);
- }
-
- public AuthAPIStub(String domain, String clientId, String clientSecret) {
- super(domain, clientId, clientSecret);
- }
-
- @Override
- public Request pushedAuthorizationRequest(String redirectUri, String responseType, Map params) {
- return pushedAuthorizationResponseRequest;
- }
- }
}
diff --git a/src/test/java/com/auth0/InvalidRequestExceptionTest.java b/src/test/java/com/auth0/InvalidRequestExceptionTest.java
index e513d58..848c01a 100644
--- a/src/test/java/com/auth0/InvalidRequestExceptionTest.java
+++ b/src/test/java/com/auth0/InvalidRequestExceptionTest.java
@@ -15,10 +15,9 @@ public void setUp() {
exception = new InvalidRequestException("error", "message");
}
- @SuppressWarnings("deprecation")
@Test
- public void shouldGetDescription() {
- assertThat(exception.getDescription(), is("message"));
+ public void shouldGetMessage() {
+ assertThat(exception.getMessage(), is("message"));
}
@Test
diff --git a/src/test/java/com/auth0/RandomStorageTest.java b/src/test/java/com/auth0/RandomStorageTest.java
deleted file mode 100644
index 49a4af7..0000000
--- a/src/test/java/com/auth0/RandomStorageTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.auth0;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-public class RandomStorageTest {
-
- @Test
- public void shouldSetState() {
- MockHttpServletRequest req = new MockHttpServletRequest();
-
- RandomStorage.setSessionState(req, "123456");
- assertThat(req.getSession().getAttribute("com.auth0.state"), is("123456"));
- }
-
- @Test
- public void shouldAcceptBothNullStates() {
- MockHttpServletRequest req = new MockHttpServletRequest();
- boolean validState = RandomStorage.checkSessionState(req, null);
- assertThat(validState, is(true));
- }
-
- @Test
- public void shouldFailIfSessionStateIsNullButCurrentStateNotNull() {
- MockHttpServletRequest req = new MockHttpServletRequest();
- boolean validState = RandomStorage.checkSessionState(req, "12345");
- assertThat(validState, is(false));
- }
-
- @Test
- public void shouldCheckAndRemoveInvalidState() {
- MockHttpServletRequest req = new MockHttpServletRequest();
- req.getSession().setAttribute("com.auth0.state", "123456");
-
- boolean validState = RandomStorage.checkSessionState(req, "abcdef");
- assertThat(validState, is(false));
- assertThat(req.getSession().getAttribute("com.auth0.state"), is(nullValue()));
- }
-
- @Test
- public void shouldCheckAndRemoveCorrectState() {
- MockHttpServletRequest req = new MockHttpServletRequest();
- req.getSession().setAttribute("com.auth0.state", "123456");
-
- boolean validState = RandomStorage.checkSessionState(req, "123456");
- assertThat(validState, is(true));
- assertThat(req.getSession().getAttribute("com.auth0.state"), is(nullValue()));
- }
-
- @Test
- public void shouldSetNonce() {
- MockHttpServletRequest req = new MockHttpServletRequest();
-
- RandomStorage.setSessionNonce(req, "123456");
- assertThat(req.getSession().getAttribute("com.auth0.nonce"), is("123456"));
- }
-
- @Test
- public void shouldGetAndRemoveNonce() {
- MockHttpServletRequest req = new MockHttpServletRequest();
- req.getSession().setAttribute("com.auth0.nonce", "123456");
-
- String nonce = RandomStorage.removeSessionNonce(req);
- assertThat(nonce, is("123456"));
- assertThat(req.getSession().getAttribute("com.auth0.nonce"), is(nullValue()));
- }
-
- @Test
- public void shouldGetAndRemoveNonceIfMissing() {
- MockHttpServletRequest req = new MockHttpServletRequest();
-
- String nonce = RandomStorage.removeSessionNonce(req);
- assertThat(nonce, is(nullValue()));
- assertThat(req.getSession().getAttribute("com.auth0.nonce"), is(nullValue()));
- }
-}
diff --git a/src/test/java/com/auth0/RequestProcessorTest.java b/src/test/java/com/auth0/RequestProcessorTest.java
index 7205b37..c719dea 100644
--- a/src/test/java/com/auth0/RequestProcessorTest.java
+++ b/src/test/java/com/auth0/RequestProcessorTest.java
@@ -1,23 +1,26 @@
package com.auth0;
-import com.auth0.client.HttpOptions;
import com.auth0.client.auth.AuthAPI;
+import com.auth0.exception.Auth0Exception;
import com.auth0.json.auth.TokenHolder;
+import com.auth0.net.Response;
import com.auth0.net.TokenRequest;
-import com.auth0.net.Telemetry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
@@ -40,27 +43,20 @@ public class RequestProcessorTest {
@Mock
private SignatureVerifier mockSignatureVerifier;
@Mock
- private IdTokenVerifier mockIdTokenVerifier;
- @Mock
- private HttpOptions mockHttpOptions;
- @Mock
private AuthAPI mockAuthAPI;
@Mock
private TokenRequest mockTokenRequest;
@Mock
+ private Response mockTokenResponse;
+ @Mock
private TokenHolder mockTokenHolder;
- @Captor
- private ArgumentCaptor stringCaptor;
- @Captor
- private ArgumentCaptor verifyOptionsCaptor;
-
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@BeforeEach
public void setUp() {
- MockitoAnnotations.initMocks(this);
+ MockitoAnnotations.openMocks(this);
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
request.setSecure(true);
@@ -75,7 +71,6 @@ public void shouldBuildRequestProcessorWithRequiredParameters() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.build();
@@ -89,7 +84,6 @@ public void shouldBuildRequestProcessorWithAllOptionalParameters() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withClockSkew(120)
.withAuthenticationMaxAge(3600)
@@ -102,6 +96,8 @@ public void shouldBuildRequestProcessorWithAllOptionalParameters() {
assertThat(processor, is(notNullValue()));
}
+ // --- Legacy SameSite Cookie Tests ---
+
@Test
public void shouldSetDefaultLegacySameSiteCookieToTrue() {
RequestProcessor processor = createDefaultRequestProcessor();
@@ -116,7 +112,6 @@ public void shouldDisableLegacySameSiteCookie() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withLegacySameSiteCookie(false)
.build();
@@ -142,32 +137,8 @@ public void shouldGetDomainFromProvider() {
}
@Test
- public void shouldCreateClientForDomainWithHttpOptions() {
- HttpOptions httpOptions = new HttpOptions();
- RequestProcessor processor = new RequestProcessor.Builder(
- mockDomainProvider,
- RESPONSE_TYPE_CODE,
- CLIENT_ID,
- CLIENT_SECRET,
- httpOptions,
- mockSignatureVerifier)
- .build();
-
- AuthAPI result = processor.createClientForDomain(DOMAIN);
-
- assertThat(result, is(notNullValue()));
- }
-
- @Test
- public void shouldCreateClientForDomainWithoutHttpOptions() {
- RequestProcessor processor = new RequestProcessor.Builder(
- mockDomainProvider,
- RESPONSE_TYPE_CODE,
- CLIENT_ID,
- CLIENT_SECRET,
- null,
- mockSignatureVerifier)
- .build();
+ public void shouldCreateClientForDomain() {
+ RequestProcessor processor = createDefaultRequestProcessor();
AuthAPI result = processor.createClientForDomain(DOMAIN);
@@ -178,14 +149,7 @@ public void shouldCreateClientForDomainWithoutHttpOptions() {
@Test
public void shouldSetLoggingEnabled() {
- RequestProcessor processor = new RequestProcessor.Builder(
- mockDomainProvider,
- RESPONSE_TYPE_CODE,
- CLIENT_ID,
- CLIENT_SECRET,
- null,
- mockSignatureVerifier)
- .build();
+ RequestProcessor processor = createDefaultRequestProcessor();
processor.setLoggingEnabled(true);
@@ -195,14 +159,7 @@ public void shouldSetLoggingEnabled() {
@Test
public void shouldDisableTelemetry() {
- RequestProcessor processor = new RequestProcessor.Builder(
- mockDomainProvider,
- RESPONSE_TYPE_CODE,
- CLIENT_ID,
- CLIENT_SECRET,
- null,
- mockSignatureVerifier)
- .build();
+ RequestProcessor processor = createDefaultRequestProcessor();
processor.doNotSendTelemetry();
@@ -210,24 +167,6 @@ public void shouldDisableTelemetry() {
assertThat(client, is(notNullValue()));
}
- @Test
- public void shouldSetupTelemetryWithVersion() {
- RequestProcessor processor = createDefaultRequestProcessor();
-
- processor.setupTelemetry(mockAuthAPI);
-
- verify(mockAuthAPI).setTelemetry(any(Telemetry.class));
- }
-
- @Test
- public void shouldReturnNullPackageVersionInDevEnvironment() {
- RequestProcessor processor = createDefaultRequestProcessor();
-
- String version = processor.obtainPackageVersion();
-
- assertThat(version, is(nullValue()));
- }
-
// --- Response Type Parsing Tests ---
@Test
@@ -299,6 +238,252 @@ public void shouldNotRequireFormPostForNullResponseType() {
assertThat(requiresFormPost, is(false));
}
+ // --- Error Handling Tests ---
+
+ @Test
+ public void shouldThrowOnProcessIfRequestHasError() {
+ request.setParameter("error", "access_denied");
+ request.setParameter("error_description", "The user denied the request");
+
+ RequestProcessor processor = createDefaultRequestProcessor();
+
+ InvalidRequestException e = assertThrows(
+ InvalidRequestException.class,
+ () -> processor.process(request, response));
+
+ assertThat(e.getCode(), is("access_denied"));
+ assertThat(e.getMessage(), is("The user denied the request"));
+ }
+
+ @Test
+ public void shouldThrowOnProcessIfRequestHasErrorWithDescription() {
+ Map params = new HashMap<>();
+ params.put("error", "something happened");
+ params.put("error_description", "something happened description");
+ MockHttpServletRequest request = getRequest(params);
+
+ RequestProcessor handler = createDefaultRequestProcessor();
+
+ InvalidRequestException e = assertThrows(InvalidRequestException.class, () -> handler.process(request, response));
+ assertThat(e.getCode(), is("something happened"));
+ assertThat(e.getMessage(), is("something happened description"));
+ }
+
+ @Test
+ public void shouldThrowOnProcessIfRequestHasInvalidStateInCookie() {
+ Map params = new HashMap<>();
+ params.put("state", "1234");
+ MockHttpServletRequest request = getRequest(params);
+ request.setCookies(new Cookie("com.auth0.state", "9999"));
+
+ RequestProcessor handler = createDefaultRequestProcessor();
+
+ InvalidRequestException e = assertThrows(InvalidRequestException.class, () -> handler.process(request, response));
+ assertThat(e.getCode(), is("a0.invalid_state"));
+ assertThat(e.getMessage(), is("The received state doesn't match the expected one."));
+ }
+
+ @Test
+ public void shouldThrowOnProcessIfRequestHasMissingStateParameter() {
+ MockHttpServletRequest request = getRequest(Collections.emptyMap());
+ request.setCookies(new Cookie("com.auth0.state", "1234"));
+
+ RequestProcessor handler = createDefaultRequestProcessor();
+
+ InvalidRequestException e = assertThrows(InvalidRequestException.class, () -> handler.process(request, response));
+ assertThat(e.getCode(), is("a0.invalid_state"));
+ assertThat(e.getMessage(), is("The received state doesn't match the expected one. No state parameter was found on the authorization response."));
+ }
+
+ @Test
+ public void shouldThrowOnProcessIfRequestHasMissingStateCookie() {
+ Map params = new HashMap<>();
+ params.put("state", "1234");
+ MockHttpServletRequest request = getRequest(params);
+
+ RequestProcessor handler = createDefaultRequestProcessor();
+
+ InvalidRequestException e = assertThrows(InvalidRequestException.class, () -> handler.process(request, response));
+ assertThat(e.getCode(), is("a0.invalid_state"));
+ }
+
+ @Test
+ public void shouldThrowOnProcessIfIdTokenRequestIsMissingIdToken() {
+ when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
+
+ Map params = new HashMap<>();
+ params.put("state", "1234");
+ MockHttpServletRequest request = getRequest(params);
+ request.setCookies(new Cookie("com.auth0.state", "1234"));
+
+ RequestProcessor handler = createRequestProcessorWithResponseType(RESPONSE_TYPE_ID_TOKEN);
+
+ InvalidRequestException e = assertThrows(InvalidRequestException.class, () -> handler.process(request, response));
+ assertThat(e.getCode(), is("a0.missing_id_token"));
+ assertThat(e.getMessage(), is("ID Token is missing from the response."));
+ }
+
+ @Test
+ public void shouldThrowOnProcessIfTokenRequestIsMissingAccessToken() {
+ when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
+
+ Map params = new HashMap<>();
+ params.put("state", "1234");
+ MockHttpServletRequest request = getRequest(params);
+ request.setCookies(new Cookie("com.auth0.state", "1234"));
+
+ RequestProcessor handler = createRequestProcessorWithResponseType(RESPONSE_TYPE_TOKEN);
+
+ InvalidRequestException e = assertThrows(InvalidRequestException.class, () -> handler.process(request, response));
+ assertThat(e.getCode(), is("a0.missing_access_token"));
+ assertThat(e.getMessage(), is("Access Token is missing from the response."));
+ }
+
+ // --- Code Exchange Flow Tests ---
+
+ @Test
+ public void shouldThrowOnProcessIfCodeRequestFailsToExecuteCodeExchange() throws Exception {
+ when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
+
+ Map params = new HashMap<>();
+ params.put("code", "abc123");
+ params.put("state", "1234");
+ MockHttpServletRequest request = getRequest(params);
+ request.setCookies(new Cookie("com.auth0.state", "1234"));
+
+ when(mockTokenRequest.execute()).thenThrow(Auth0Exception.class);
+ when(mockAuthAPI.exchangeCode(eq("abc123"), anyString())).thenReturn(mockTokenRequest);
+
+ RequestProcessor handler = createDefaultRequestProcessor();
+ RequestProcessor spy = spy(handler);
+ doReturn(mockAuthAPI).when(spy).createClientForDomain(anyString());
+
+ IdentityVerificationException e = assertThrows(IdentityVerificationException.class, () -> spy.process(request, response));
+ assertThat(e.getCode(), is("a0.api_error"));
+ assertThat(e.getMessage(), is("An error occurred while exchanging the authorization code."));
+ }
+
+ @Test
+ public void shouldThrowOnProcessIfCodeRequestSucceedsButDoesNotPassIdTokenVerification() throws Exception {
+ when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
+
+ Map params = new HashMap<>();
+ params.put("code", "abc123");
+ params.put("state", "1234");
+ MockHttpServletRequest request = getRequest(params);
+ request.setCookies(new Cookie("com.auth0.state", "1234"));
+
+ // Return a structurally valid JWT with wrong issuer so verification fails
+ String fakeJwt = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3dyb25nLyIsInN1YiI6InVzZXIxMjMiLCJhdWQiOiJ0ZXN0Q2xpZW50SWQiLCJleHAiOjk5OTk5OTk5OTksImlhdCI6MTYwMDAwMDAwMH0.signature";
+ when(mockTokenHolder.getIdToken()).thenReturn(fakeJwt);
+ when(mockTokenResponse.getBody()).thenReturn(mockTokenHolder);
+ when(mockTokenRequest.execute()).thenReturn(mockTokenResponse);
+ when(mockAuthAPI.exchangeCode(eq("abc123"), anyString())).thenReturn(mockTokenRequest);
+
+ // Use real AlgorithmNameVerifier so signature check passes but claim validation fails
+ RequestProcessor handler = new RequestProcessor.Builder(
+ mockDomainProvider,
+ RESPONSE_TYPE_CODE,
+ CLIENT_ID,
+ CLIENT_SECRET,
+ new AlgorithmNameVerifier())
+ .build();
+ RequestProcessor spy = spy(handler);
+ doReturn(mockAuthAPI).when(spy).createClientForDomain(anyString());
+
+ IdentityVerificationException e = assertThrows(IdentityVerificationException.class, () -> spy.process(request, response));
+ assertThat(e.getCode(), is("a0.invalid_jwt_error"));
+ assertThat(e.getMessage(), is("An error occurred while trying to verify the ID Token."));
+ }
+
+ @Test
+ public void shouldReturnTokensOnProcessIfCodeRequestSucceeds() throws Exception {
+ when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
+
+ Map params = new HashMap<>();
+ params.put("code", "abc123");
+ params.put("state", "1234");
+ MockHttpServletRequest request = getRequest(params);
+ request.setCookies(new Cookie("com.auth0.state", "1234"));
+
+ // Return no ID token so verification is skipped
+ when(mockTokenHolder.getIdToken()).thenReturn(null);
+ when(mockTokenHolder.getAccessToken()).thenReturn("backAccessToken");
+ when(mockTokenHolder.getRefreshToken()).thenReturn("backRefreshToken");
+ when(mockTokenHolder.getTokenType()).thenReturn("Bearer");
+ when(mockTokenHolder.getExpiresIn()).thenReturn(3600L);
+ when(mockTokenResponse.getBody()).thenReturn(mockTokenHolder);
+ when(mockTokenRequest.execute()).thenReturn(mockTokenResponse);
+ when(mockAuthAPI.exchangeCode(eq("abc123"), anyString())).thenReturn(mockTokenRequest);
+
+ RequestProcessor handler = createDefaultRequestProcessor();
+ RequestProcessor spy = spy(handler);
+ doReturn(mockAuthAPI).when(spy).createClientForDomain(anyString());
+
+ Tokens tokens = spy.process(request, response);
+
+ assertThat(tokens, is(notNullValue()));
+ assertThat(tokens.getAccessToken(), is("backAccessToken"));
+ assertThat(tokens.getRefreshToken(), is("backRefreshToken"));
+ assertThat(tokens.getType(), is("Bearer"));
+ assertThat(tokens.getExpiresIn(), is(3600L));
+ }
+
+ @Test
+ public void shouldReturnEmptyTokensWhenCodeRequestReturnsNoTokens() throws Exception {
+ when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
+
+ Map params = new HashMap<>();
+ params.put("code", "abc123");
+ params.put("state", "1234");
+ MockHttpServletRequest request = getRequest(params);
+ request.setCookies(new Cookie("com.auth0.state", "1234"));
+
+ when(mockTokenResponse.getBody()).thenReturn(mockTokenHolder);
+ when(mockTokenRequest.execute()).thenReturn(mockTokenResponse);
+ when(mockAuthAPI.exchangeCode(eq("abc123"), anyString())).thenReturn(mockTokenRequest);
+
+ RequestProcessor handler = createDefaultRequestProcessor();
+ RequestProcessor spy = spy(handler);
+ doReturn(mockAuthAPI).when(spy).createClientForDomain(anyString());
+
+ Tokens tokens = spy.process(request, response);
+
+ assertThat(tokens, is(notNullValue()));
+ assertThat(tokens.getIdToken(), is(nullValue()));
+ assertThat(tokens.getAccessToken(), is(nullValue()));
+ assertThat(tokens.getRefreshToken(), is(nullValue()));
+ }
+
+ // --- Implicit / Hybrid Flow Tests ---
+
+ @Test
+ public void shouldThrowOnProcessIfIdTokenRequestDoesNotPassIdTokenVerification() {
+ when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
+
+ // Structurally valid JWT with wrong issuer so claim validation fails
+ String fakeJwt = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3dyb25nLyIsInN1YiI6InVzZXIxMjMiLCJhdWQiOiJ0ZXN0Q2xpZW50SWQiLCJleHAiOjk5OTk5OTk5OTksImlhdCI6MTYwMDAwMDAwMH0.signature";
+
+ Map params = new HashMap<>();
+ params.put("state", "1234");
+ params.put("id_token", fakeJwt);
+ MockHttpServletRequest request = getRequest(params);
+ request.setCookies(new Cookie("com.auth0.state", "1234"));
+
+ // Use real AlgorithmNameVerifier so signature check passes but claim validation fails
+ RequestProcessor handler = new RequestProcessor.Builder(
+ mockDomainProvider,
+ RESPONSE_TYPE_ID_TOKEN,
+ CLIENT_ID,
+ CLIENT_SECRET,
+ new AlgorithmNameVerifier())
+ .build();
+
+ IdentityVerificationException e = assertThrows(IdentityVerificationException.class, () -> handler.process(request, response));
+ assertThat(e.getCode(), is("a0.invalid_jwt_error"));
+ assertThat(e.getMessage(), is("An error occurred while trying to verify the ID Token."));
+ }
+
// --- AuthorizeUrl Building Tests ---
@Test
@@ -322,7 +507,6 @@ public void shouldBuildAuthorizeUrlWithOrganization() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withOrganization("org_123")
.build();
@@ -343,7 +527,6 @@ public void shouldBuildAuthorizeUrlWithInvitation() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withInvitation("inv_456")
.build();
@@ -364,7 +547,6 @@ public void shouldBuildAuthorizeUrlWithCustomCookiePath() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withCookiePath("/custom")
.build();
@@ -377,111 +559,28 @@ public void shouldBuildAuthorizeUrlWithCustomCookiePath() {
assertThat(result, is(notNullValue()));
}
- // --- Error Handling Tests ---
-
- @Test
- public void shouldThrowExceptionWhenErrorInRequest() {
- request.setParameter("error", "access_denied");
- request.setParameter("error_description", "The user denied the request");
-
- RequestProcessor processor = createDefaultRequestProcessor();
-
- InvalidRequestException exception = assertThrows(
- InvalidRequestException.class,
- () -> processor.process(request, response));
-
- assertThat(exception.getCode(), is("access_denied"));
- assertThat(exception.getMessage(), is("The user denied the request"));
- }
-
- @Test
- public void shouldThrowExceptionWhenStateIsMissing() {
- request.setParameter("code", "test_code");
-
- RequestProcessor processor = createDefaultRequestProcessor();
-
- InvalidRequestException exception = assertThrows(
- InvalidRequestException.class,
- () -> processor.process(request, response));
-
- assertThat(exception.getCode(), is("a0.invalid_state"));
- }
-
- @Test
- public void shouldThrowExceptionWhenIdTokenMissingForImplicitGrant() {
- request.setParameter("state", "validState");
-
- RequestProcessor processor = createRequestProcessorWithResponseType(RESPONSE_TYPE_ID_TOKEN);
-
- InvalidRequestException exception = assertThrows(
- InvalidRequestException.class,
- () -> processor.process(request, response));
-
- assertThat(exception, is(notNullValue()));
- assertThat(exception.getCode(), is(notNullValue()));
- }
-
@Test
- public void shouldThrowExceptionWhenAccessTokenMissingForTokenGrant() {
- request.setParameter("state", "validState");
-
- RequestProcessor processor = createRequestProcessorWithResponseType(RESPONSE_TYPE_TOKEN);
+ public void shouldBuildAuthorizeUrlWithFormPostIfResponseTypeIsToken() {
+ when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
+ RequestProcessor handler = createRequestProcessorWithResponseType(RESPONSE_TYPE_TOKEN);
+ RequestProcessor spy = spy(handler);
+ doReturn(mockAuthAPI).when(spy).createClientForDomain(anyString());
- InvalidRequestException exception = assertThrows(
- InvalidRequestException.class,
- () -> processor.process(request, response));
+ AuthorizeUrl result = spy.buildAuthorizeUrl(request, response, "https://redirect.uri/here", "state", "nonce");
- assertThat(exception, is(notNullValue()));
- assertThat(exception.getCode(), is(notNullValue()));
+ assertThat(result, is(notNullValue()));
}
- // --- Token Processing Tests ---
-
@Test
- public void shouldProcessCodeGrantFlow() throws Exception {
- request.setParameter("code", "auth_code_123");
- request.setParameter("state", "validState");
-
- RequestProcessor processor = createDefaultRequestProcessor();
- RequestProcessor spy = spy(processor);
-
+ public void shouldBuildAuthorizeUrlWithNonceAndFormPostIfResponseTypeIsIdToken() {
+ when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
+ RequestProcessor handler = createRequestProcessorWithResponseType(RESPONSE_TYPE_ID_TOKEN);
+ RequestProcessor spy = spy(handler);
doReturn(mockAuthAPI).when(spy).createClientForDomain(anyString());
- when(mockAuthAPI.exchangeCode(anyString(), anyString())).thenReturn(mockTokenRequest);
- when(mockTokenRequest.execute()).thenReturn(mockTokenHolder);
- when(mockTokenHolder.getAccessToken()).thenReturn("access_token_123");
- try {
- Tokens result = spy.process(request, response);
- assertThat(result, is(notNullValue()));
- } catch (InvalidRequestException e) {
- // Expected due to state cookie validation
- assertThat(e.getCode(), is(notNullValue()));
- }
- }
-
- @Test
- public void shouldProcessImplicitGrantFlow() throws Exception {
- request.setParameter("access_token", "access_token_123");
- request.setParameter("id_token", createMockIdToken());
- request.setParameter("token_type", "Bearer");
- request.setParameter("expires_in", "3600");
- request.setParameter("state", "validState");
+ AuthorizeUrl result = spy.buildAuthorizeUrl(request, response, "https://redirect.uri/here", "state", "nonce");
- response.addCookie(new javax.servlet.http.Cookie("com.auth0.state", "validState"));
-
- RequestProcessor processor = createRequestProcessorWithResponseType("id_token token");
-
- try {
- Tokens result = processor.process(request, response);
- assertThat(result, is(notNullValue()));
- assertThat(result.getAccessToken(), is("access_token_123"));
- assertThat(result.getIdToken(), is(notNullValue()));
- assertThat(result.getType(), is("Bearer"));
- assertThat(result.getExpiresIn(), is(3600L));
- } catch (IdentityVerificationException e) {
- // Expected due to token verification
- assertThat(e, is(notNullValue()));
- }
+ assertThat(result, is(notNullValue()));
}
// --- Builder Configuration Tests ---
@@ -493,7 +592,6 @@ public void shouldSupportOrganizationParameter() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withOrganization("org_123")
.build();
@@ -508,7 +606,6 @@ public void shouldSupportInvitationParameter() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withInvitation("inv_456")
.build();
@@ -523,7 +620,6 @@ public void shouldSupportCustomCookiePath() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withCookiePath("/custom/path")
.build();
@@ -538,7 +634,6 @@ public void shouldSupportClockSkewConfiguration() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withClockSkew(180)
.build();
@@ -553,7 +648,6 @@ public void shouldSupportAuthenticationMaxAge() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.withAuthenticationMaxAge(7200)
.build();
@@ -569,7 +663,6 @@ private RequestProcessor createDefaultRequestProcessor() {
RESPONSE_TYPE_CODE,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.build();
}
@@ -580,17 +673,17 @@ private RequestProcessor createRequestProcessorWithResponseType(String responseT
responseType,
CLIENT_ID,
CLIENT_SECRET,
- mockHttpOptions,
mockSignatureVerifier)
.build();
}
- private String createMockIdToken() {
- String header = java.util.Base64.getUrlEncoder().withoutPadding()
- .encodeToString("{\"typ\":\"JWT\",\"alg\":\"RS256\"}".getBytes());
- String payload = java.util.Base64.getUrlEncoder().withoutPadding()
- .encodeToString(("{\"iss\":\"https://" + DOMAIN + "/\",\"sub\":\"user123\"}").getBytes());
- String signature = "signature";
- return header + "." + payload + "." + signature;
+ private MockHttpServletRequest getRequest(Map parameters) {
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setScheme("https");
+ request.setServerName("me.auth0.com");
+ request.setServerPort(80);
+ request.setRequestURI("/callback");
+ request.setParameters(parameters);
+ return request;
}
}
diff --git a/src/test/java/com/auth0/SessionUtilsTest.java b/src/test/java/com/auth0/SessionUtilsTest.java
deleted file mode 100644
index d7edf62..0000000
--- a/src/test/java/com/auth0/SessionUtilsTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.auth0;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-public class SessionUtilsTest {
- @Test
- public void shouldGetAndRemoveAttribute() {
- MockHttpServletRequest req = new MockHttpServletRequest();
- req.getSession().setAttribute("name", "value");
-
- assertThat(SessionUtils.remove(req, "name"), is("value"));
- assertThat(req.getSession().getAttribute("name"), is(nullValue()));
- }
-
- @Test
- public void shouldGetAttribute() {
- MockHttpServletRequest req = new MockHttpServletRequest();
- req.getSession().setAttribute("name", "value");
-
- assertThat(SessionUtils.get(req, "name"), is("value"));
- assertThat(req.getSession().getAttribute("name"), is("value"));
- }
-
- @Test
- public void shouldGetNullAttributeIfMissing() {
- MockHttpServletRequest req = new MockHttpServletRequest();
-
- assertThat(SessionUtils.get(req, "name"), is(nullValue()));
- assertThat(req.getSession().getAttribute("name"), is(nullValue()));
- }
-
- @Test
- public void shouldSetAttribute() {
- MockHttpServletRequest req = new MockHttpServletRequest();
-
- SessionUtils.set(req, "name", "value");
- assertThat(req.getSession().getAttribute("name"), is("value"));
- }
-
-}
diff --git a/src/test/java/com/auth0/SignatureVerifierTest.java b/src/test/java/com/auth0/SignatureVerifierTest.java
index 326387f..b0b6678 100644
--- a/src/test/java/com/auth0/SignatureVerifierTest.java
+++ b/src/test/java/com/auth0/SignatureVerifierTest.java
@@ -4,9 +4,9 @@
import com.auth0.jwk.JwkException;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwt.interfaces.DecodedJWT;
-import org.bouncycastle.util.io.pem.PemReader;
import org.junit.jupiter.api.Test;
+import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
@@ -18,6 +18,7 @@
import java.security.interfaces.RSAPublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
import java.util.Scanner;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -146,7 +147,7 @@ private JwkProvider getRSProvider(String rsaPath) throws Exception {
private static RSAPublicKey readPublicKeyFromFile(final String path) throws IOException {
Scanner scanner = null;
- PemReader pemReader = null;
+ BufferedReader reader = null;
try {
scanner = new Scanner(Paths.get(path));
if (scanner.hasNextLine() && scanner.nextLine().startsWith("-----BEGIN CERTIFICATE-----")) {
@@ -157,8 +158,15 @@ private static RSAPublicKey readPublicKeyFromFile(final String path) throws IOEx
fs.close();
return (RSAPublicKey) key;
} else {
- pemReader = new PemReader(new FileReader(path));
- byte[] keyBytes = pemReader.readPemObject().getContent();
+ reader = new BufferedReader(new FileReader(path));
+ StringBuilder pemContent = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (!line.startsWith("-----BEGIN") && !line.startsWith("-----END")) {
+ pemContent.append(line);
+ }
+ }
+ byte[] keyBytes = Base64.getDecoder().decode(pemContent.toString());
KeyFactory kf = KeyFactory.getInstance("RSA");
EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
return (RSAPublicKey) kf.generatePublic(keySpec);
@@ -169,8 +177,8 @@ private static RSAPublicKey readPublicKeyFromFile(final String path) throws IOEx
if (scanner != null) {
scanner.close();
}
- if (pemReader != null) {
- pemReader.close();
+ if (reader != null) {
+ reader.close();
}
}
}
diff --git a/src/test/java/com/auth0/TransientCookieStoreTest.java b/src/test/java/com/auth0/TransientCookieStoreTest.java
index 9db31f4..00f30cb 100644
--- a/src/test/java/com/auth0/TransientCookieStoreTest.java
+++ b/src/test/java/com/auth0/TransientCookieStoreTest.java
@@ -6,13 +6,14 @@
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
-import javax.servlet.http.Cookie;
+import jakarta.servlet.http.Cookie;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.matchesPattern;
public class TransientCookieStoreTest {
@@ -49,11 +50,9 @@ public void shouldHandleSpecialCharsWhenStoringState() throws Exception {
List headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(2));
- String expectedEncodedState = URLEncoder.encode(stateVal, "UTF-8");
- assertThat(headers, hasItem(
- String.format("com.auth0.state=%s; HttpOnly; Max-Age=600; SameSite=None; Secure", expectedEncodedState)));
- assertThat(headers, hasItem(
- String.format("_com.auth0.state=%s; HttpOnly; Max-Age=600", expectedEncodedState)));
+ String expectedEncodedState = URLEncoder.encode(stateVal, "UTF-8").replaceAll("\\+", "\\\\+");
+ assertThat(headers, hasItem(matchesPattern(String.format("com\\.auth0\\.state=%s; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None", expectedEncodedState))));
+ assertThat(headers, hasItem(matchesPattern(String.format("_com\\.auth0\\.state=%s; Max-Age=600; Expires=.*?; HttpOnly", expectedEncodedState))));
}
@Test
@@ -63,8 +62,8 @@ public void shouldSetStateSameSiteCookieAndFallbackCookie() {
List headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(2));
- assertThat(headers, hasItem("com.auth0.state=123456; HttpOnly; Max-Age=600; SameSite=None; Secure"));
- assertThat(headers, hasItem("_com.auth0.state=123456; HttpOnly; Max-Age=600"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.state=123456; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
+ assertThat(headers, hasItem(matchesPattern("_com\\.auth0\\.state=123456; Max-Age=600; Expires=.*?; HttpOnly")));
}
@Test
@@ -74,7 +73,7 @@ public void shouldSetStateSameSiteCookieAndNoFallbackCookie() {
List headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(1));
- assertThat(headers, hasItem("com.auth0.state=123456; HttpOnly; Max-Age=600; SameSite=None; Secure"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.state=123456; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
}
@Test
@@ -84,7 +83,7 @@ public void shouldSetSecureCookieWhenSameSiteLaxAndConfigured() {
List headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(1));
- assertThat(headers, hasItem("com.auth0.state=123456; HttpOnly; Max-Age=600; SameSite=Lax; Secure"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.state=123456; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=Lax")));
}
@Test
@@ -94,8 +93,8 @@ public void shouldSetSecureFallbackCookieWhenSameSiteNoneAndConfigured() {
List headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(2));
- assertThat(headers, hasItem("com.auth0.state=123456; HttpOnly; Max-Age=600; SameSite=None; Secure"));
- assertThat(headers, hasItem("_com.auth0.state=123456; HttpOnly; Max-Age=600; Secure"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.state=123456; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
+ assertThat(headers, hasItem(matchesPattern("_com\\.auth0\\.state=123456; Max-Age=600; Expires=.*?; Secure; HttpOnly")));
}
@Test
@@ -105,7 +104,7 @@ public void shouldNotSetSecureCookieWhenSameSiteLaxAndConfigured() {
List headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(1));
- assertThat(headers, hasItem("com.auth0.state=123456; HttpOnly; Max-Age=600; SameSite=Lax"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.state=123456; Max-Age=600; Expires=.*?; HttpOnly; SameSite=Lax")));
}
@Test
@@ -115,8 +114,8 @@ public void shouldSetNonceSameSiteCookieAndFallbackCookie() {
List headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(2));
- assertThat(headers, hasItem("com.auth0.nonce=123456; HttpOnly; Max-Age=600; SameSite=None; Secure"));
- assertThat(headers, hasItem("_com.auth0.nonce=123456; HttpOnly; Max-Age=600"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.nonce=123456; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
+ assertThat(headers, hasItem(matchesPattern("_com\\.auth0\\.nonce=123456; Max-Age=600; Expires=.*?; HttpOnly")));
}
@Test
@@ -126,7 +125,7 @@ public void shouldSetNonceSameSiteCookieAndNoFallbackCookie() {
List headers = response.getHeaders("Set-Cookie");
assertThat(headers.size(), is(1));
- assertThat(headers, hasItem("com.auth0.nonce=123456; HttpOnly; Max-Age=600; SameSite=None; Secure"));
+ assertThat(headers, hasItem(matchesPattern("com\\.auth0\\.nonce=123456; Max-Age=600; Expires=.*?; Secure; HttpOnly; SameSite=None")));
}
@Test