-
Notifications
You must be signed in to change notification settings - Fork 42
Feat: Added MCD Support #199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
|
||
| // 4. Success: Store in session (Requirement #5) | ||
| HttpSession session = req.getSession(true); | ||
| session.setAttribute("accessToken", tokens.getAccessToken()); |
Check failure
Code scanning / CodeQL
Trust boundary violation High test
remote source
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
At a high level, the problem is that CallbackServlet persists tokens.getAccessToken() (and similarly tokens.getIdToken()) directly into the HTTP session, even though the Tokens object may contain values that originated in untrusted front‑channel request parameters. To maintain a proper trust boundary, the servlet should either (a) avoid storing raw tokens in the session at all, or (b) only store fields derived from fully verified tokens and/or mark/handle them explicitly as untrusted. Since changing how AuthenticationController and RequestProcessor validate tokens would be intrusive, the least‑disruptive fix is to change CallbackServlet so it no longer stores the raw tokens in the session.
The single best fix that preserves existing behavior as much as possible while resolving the trust‑boundary violation is:
- Remove the lines that write
accessTokenandidTokendirectly into the session. - If the session needs some indication that the user is authenticated, store only a minimal, derived flag (e.g., a boolean
"authenticated"attribute) that does not carry attacker‑controlled content. This avoids persisting the token values themselves, so even if an attacker tampers with the access token parameter, it won’t be stored and reused by server‑side code. - Leave the rest of the logic (using
AuthenticationController.handle, printing the domain, and writing the success message) unchanged, since the CodeQL alert is specifically about the session write.
Concretely, in src/main/java/com/auth0/test/CallbackServlet.java, within doGet, replace:
HttpSession session = req.getSession(true);
session.setAttribute("accessToken", tokens.getAccessToken());
session.setAttribute("idToken", tokens.getIdToken());with something like:
HttpSession session = req.getSession(true);
// Mark the user as authenticated without storing raw token values in the session.
session.setAttribute("authenticated", Boolean.TRUE);This change removes the tainted data flow into the session while still giving downstream code a way to know that an authentication step has succeeded. No additional imports or helper methods are required.
-
Copy modified line R23 -
Copy modified line R25
| @@ -20,10 +20,9 @@ | ||
| // and performs the dynamic ID token verification. | ||
| Tokens tokens = controller.handle(req, resp); | ||
|
|
||
| // 4. Success: Store in session (Requirement #5) | ||
| // 4. Success: Store authentication state in session without persisting raw token values | ||
| HttpSession session = req.getSession(true); | ||
| session.setAttribute("accessToken", tokens.getAccessToken()); | ||
| session.setAttribute("idToken", tokens.getIdToken()); | ||
| session.setAttribute("authenticated", Boolean.TRUE); | ||
|
|
||
| // Note: originDomain is now inside the tokens object | ||
| System.out.println("Authenticated via domain: " + tokens.getDomain()); |
| // 4. Success: Store in session (Requirement #5) | ||
| HttpSession session = req.getSession(true); | ||
| session.setAttribute("accessToken", tokens.getAccessToken()); | ||
| session.setAttribute("idToken", tokens.getIdToken()); |
Check failure
Code scanning / CodeQL
Trust boundary violation High test
remote source
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
In general terms, we should avoid directly trusting or propagating unvalidated data from HTTP request parameters into a more trusted context (like the HTTP session or HTML responses). For this specific case, there are two issues: (1) storing the raw idToken string (originating from request parameters) in the session; and (2) writing the raw idToken back to the response body. The ID token itself is cryptographically verified inside RequestProcessor, but the servlet layer is not aware of that and CodeQL flags the direct flow. We can resolve the finding and improve security by (a) no longer storing the raw ID token in the session and (b) not echoing it back to the client.
The best fix that preserves core functionality is to store only the access token (which is already being stored) and to remove direct use of tokens.getIdToken() for session storage and response output in CallbackServlet. Typically, the ID token is used server-side to derive user identity (name/email/subject) rather than being stored as-is and printed to the user; however, since we must not change the rest of the codebase, we will simply stop persisting and echoing the raw token. This eliminates the tainted flow to the session and to the response while leaving the Tokens class and RequestProcessor behavior untouched.
Concretely:
- In
src/main/java/com/auth0/test/CallbackServlet.java:- Remove
session.setAttribute("idToken", tokens.getIdToken());. - Change the success message so that it does not concatenate
tokens.getIdToken()(e.g., just “Login Successful!” or a generic welcome string).
No changes are needed inTokens,RequestProcessor, orAuthenticationControllerfor this specific trust-boundary finding.
- Remove
-
Copy modified line R30
| @@ -23,12 +23,11 @@ | ||
| // 4. Success: Store in session (Requirement #5) | ||
| HttpSession session = req.getSession(true); | ||
| session.setAttribute("accessToken", tokens.getAccessToken()); | ||
| session.setAttribute("idToken", tokens.getIdToken()); | ||
|
|
||
| // Note: originDomain is now inside the tokens object | ||
| System.out.println("Authenticated via domain: " + tokens.getDomain()); | ||
|
|
||
| resp.getWriter().write("Login Successful! Welcome, " + tokens.getIdToken()); | ||
| resp.getWriter().write("Login Successful!"); | ||
|
|
||
| } catch (IdentityVerificationException e) { | ||
| resp.setStatus(HttpServletResponse.SC_FORBIDDEN); |
| // Note: originDomain is now inside the tokens object | ||
| System.out.println("Authenticated via domain: " + tokens.getDomain()); | ||
|
|
||
| resp.getWriter().write("Login Successful! Welcome, " + tokens.getIdToken()); |
Check warning
Code scanning / CodeQL
Cross-site scripting Medium test
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
General fix: Never write untrusted values directly to the HTTP response without appropriate output encoding. In this case, you should not display the raw idToken at all. Instead, either (a) remove the token from the user-facing message entirely, or (b) display a safe, HTML-escaped value derived from trusted/validated data.
Best fix with minimal functional change: Adjust CallbackServlet.doGet so that the success message no longer includes tokens.getIdToken(). The simplest and safest change is to return a generic success message like "Login Successful!" or "Login Successful! Welcome." without any user-controlled content. This preserves the logical behavior (successful login, session attributes set) while eliminating the XSS sink. No other classes (Tokens, RequestProcessor, AuthenticationController) need modification for this issue.
Concretely:
- In
src/main/java/com/auth0/test/CallbackServlet.java, update line 31:- Replace
resp.getWriter().write("Login Successful! Welcome, " + tokens.getIdToken()); - With a neutral message such as
resp.getWriter().write("Login Successful!");(or similar, but without includingidTokenor any other request-derived data).
- Replace
- No new methods or imports are required for this minimal fix.
If in the future you want to display user attributes (e.g., a user’s name), you should:
- Decode the ID token into claims on the server side, and
- HTML-escape any claim values before including them in the response (using a well-known library like Apache Commons Text’s
StringEscapeUtils.escapeHtml4), and - Only display non-sensitive, user-facing fields (never raw tokens).
-
Copy modified line R31
| @@ -28,7 +28,7 @@ | ||
| // Note: originDomain is now inside the tokens object | ||
| System.out.println("Authenticated via domain: " + tokens.getDomain()); | ||
|
|
||
| resp.getWriter().write("Login Successful! Welcome, " + tokens.getIdToken()); | ||
| resp.getWriter().write("Login Successful!"); | ||
|
|
||
| } catch (IdentityVerificationException e) { | ||
| resp.setStatus(HttpServletResponse.SC_FORBIDDEN); |
|
|
||
| } catch (IdentityVerificationException e) { | ||
| resp.setStatus(HttpServletResponse.SC_FORBIDDEN); | ||
| resp.getWriter().write("Authentication failed: " + e.getMessage()); |
Check warning
Code scanning / CodeQL
Information exposure through an error message Medium test
Error information
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
In general, to fix this class of issues, you should avoid sending raw exception messages or stack traces to the client. Instead, log the detailed error server-side and return a generic, non-revealing message (or appropriate HTTP status code) to the user.
For this specific servlet, the best fix without changing functionality is:
- Keep returning HTTP 403 (forbidden) on authentication failures.
- Replace
e.getMessage()in the response body with a generic message like"Authentication failed"or"Authentication failed. Please try again.". - Optionally log the exception on the server using standard Java logging so developers can still see the root cause.
We are only allowed to modify src/main/java/com/auth0/test/CallbackServlet.java within the shown snippet. To log the error, we can either:
- Use
e.printStackTrace();(not ideal stylistically, but requires no new imports), or - Add
java.util.logging.Loggerand use that.
Adding java.util.logging.Logger is a standard, well-known library and fits the constraints. Concretely:
- Add a
private static final Logger LOGGER = Logger.getLogger(CallbackServlet.class.getName());field. - In the catch block, log the exception with
LOGGER.log(Level.WARNING, "Authentication failed", e);. - Change the line writing the response to:
resp.getWriter().write("Authentication failed");.
This preserves existing behavior (403 on auth failure, success path unchanged) while preventing detailed error information from reaching the client.
-
Copy modified lines R9-R10 -
Copy modified lines R15-R16 -
Copy modified line R34 -
Copy modified line R36
| @@ -6,10 +6,14 @@ | ||
| import javax.servlet.annotation.WebServlet; | ||
| import javax.servlet.http.*; | ||
| import java.io.IOException; | ||
| import java.util.logging.Level; | ||
| import java.util.logging.Logger; | ||
|
|
||
| @WebServlet(urlPatterns = {"/callback"}) | ||
| public class CallbackServlet extends HttpServlet { | ||
|
|
||
| private static final Logger LOGGER = Logger.getLogger(CallbackServlet.class.getName()); | ||
|
|
||
| @Override | ||
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { | ||
| AuthenticationController controller = Auth0Provider.getController(); | ||
| @@ -31,8 +31,9 @@ | ||
| resp.getWriter().write("Login Successful! Welcome, " + tokens.getIdToken()); | ||
|
|
||
| } catch (IdentityVerificationException e) { | ||
| LOGGER.log(Level.WARNING, "Authentication failed", e); | ||
| resp.setStatus(HttpServletResponse.SC_FORBIDDEN); | ||
| resp.getWriter().write("Authentication failed: " + e.getMessage()); | ||
| resp.getWriter().write("Authentication failed"); | ||
| } | ||
| } | ||
| } |
| * @param request the current HttpServletRequest | ||
| * @return a single domain string (e.g., "tenant.auth0.com") | ||
| */ | ||
| String resolve(HttpServletRequest request); |
Check notice
Code scanning / CodeQL
Useless parameter Note
Copilot Autofix
AI 2 days ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
| } | ||
|
|
||
| public Options(String audience, SignatureVerifier verifier) { | ||
| Validate.notNull(audience); |
Check notice
Code scanning / CodeQL
Deprecated method or constructor invocation Note
Validate.notNull
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
In general, to fix a deprecated method invocation you should switch to the recommended non-deprecated overload or replacement API described in the library’s documentation, keeping semantics the same. For Apache Commons Lang’s Validate.notNull(T object), the standard replacement is Validate.notNull(T object, String message) or the varargs version with a format string.
In this code, there are two constructors in IdTokenVerifier.Options using the deprecated overload:
159: Validate.notNull(issuer);
160: Validate.notNull(audience);
161: Validate.notNull(verifier);
...
168: Validate.notNull(audience);
169: Validate.notNull(verifier);The best fix is to call the non-deprecated overload that accepts a message, e.g.:
Validate.notNull(audience, "Audience must not be null");This preserves the null-check semantics, avoids deprecation, and adds helpful context if validation fails. No new imports are needed, and we shouldn’t change other parts of the file. We will update all Validate.notNull(...) calls in this shown region to include a clear, parameter-specific message while leaving the rest of the constructors untouched.
-
Copy modified lines R159-R161 -
Copy modified lines R168-R169
| @@ -156,17 +156,17 @@ | ||
| String organization; | ||
|
|
||
| public Options(String issuer, String audience, SignatureVerifier verifier) { | ||
| Validate.notNull(issuer); | ||
| Validate.notNull(audience); | ||
| Validate.notNull(verifier); | ||
| Validate.notNull(issuer, "Issuer must not be null"); | ||
| Validate.notNull(audience, "Audience must not be null"); | ||
| Validate.notNull(verifier, "SignatureVerifier must not be null"); | ||
| this.issuer = issuer; | ||
| this.audience = audience; | ||
| this.verifier = verifier; | ||
| } | ||
|
|
||
| public Options(String audience, SignatureVerifier verifier) { | ||
| Validate.notNull(audience); | ||
| Validate.notNull(verifier); | ||
| Validate.notNull(audience, "Audience must not be null"); | ||
| Validate.notNull(verifier, "SignatureVerifier must not be null"); | ||
| this.audience = audience; | ||
| this.verifier = verifier; | ||
| } |
|
|
||
| public Options(String audience, SignatureVerifier verifier) { | ||
| Validate.notNull(audience); | ||
| Validate.notNull(verifier); |
Check notice
Code scanning / CodeQL
Deprecated method or constructor invocation Note
Validate.notNull
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
In general, to fix usage of a deprecated validation method like Validate.notNull, you should replace it with a non‑deprecated equivalent that provides the same null‑checking semantics. For null checks, the idiomatic modern replacement is java.util.Objects.requireNonNull, which throws a NullPointerException when the argument is null; you can also supply a custom message.
For this specific case in IdTokenVerifier.Options, we should replace both constructor calls to Validate.notNull(...) with Objects.requireNonNull(...). This maintains the intent (ensuring issuer, audience, and verifier are non‑null) without changing functional behavior in any observable way, beyond the exact exception type/message if previously different. To implement this, within src/main/java/com/auth0/IdTokenVerifier.java:
- Add an import for
java.util.Objects;at the top, leaving the existing imports intact. - In the constructor
public Options(String issuer, String audience, SignatureVerifier verifier), replace:Validate.notNull(issuer);withObjects.requireNonNull(issuer, "issuer");Validate.notNull(audience);withObjects.requireNonNull(audience, "audience");Validate.notNull(verifier);withObjects.requireNonNull(verifier, "verifier");
- In the constructor
public Options(String audience, SignatureVerifier verifier), replace:Validate.notNull(audience);withObjects.requireNonNull(audience, "audience");Validate.notNull(verifier);withObjects.requireNonNull(verifier, "verifier");
We keep the existing Validate import untouched in this file since we’re not shown its other usages, but we stop using the deprecated notNull method in the provided snippet.
-
Copy modified line R9 -
Copy modified lines R160-R162 -
Copy modified lines R169-R170
| @@ -6,6 +6,7 @@ | ||
| import java.util.Calendar; | ||
| import java.util.Date; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
|
|
||
| /** | ||
| * Token verification utility class. | ||
| @@ -156,17 +157,17 @@ | ||
| String organization; | ||
|
|
||
| public Options(String issuer, String audience, SignatureVerifier verifier) { | ||
| Validate.notNull(issuer); | ||
| Validate.notNull(audience); | ||
| Validate.notNull(verifier); | ||
| Objects.requireNonNull(issuer, "issuer"); | ||
| Objects.requireNonNull(audience, "audience"); | ||
| Objects.requireNonNull(verifier, "verifier"); | ||
| this.issuer = issuer; | ||
| this.audience = audience; | ||
| this.verifier = verifier; | ||
| } | ||
|
|
||
| public Options(String audience, SignatureVerifier verifier) { | ||
| Validate.notNull(audience); | ||
| Validate.notNull(verifier); | ||
| Objects.requireNonNull(audience, "audience"); | ||
| Objects.requireNonNull(verifier, "verifier"); | ||
| this.audience = audience; | ||
| this.verifier = verifier; | ||
| } |
| "<CLIENT_SECRET>") | ||
| .build(); | ||
|
|
||
| System.out.println("Created AuthenticationController with MCD DomainResolver "+controller.toString()); |
Check notice
Code scanning / CodeQL
Use of default toString() Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
In general, to fix this kind of issue you either (a) implement a meaningful toString() in your own class, or (b) avoid calling toString() on third‑party types that don’t override it, and instead log only relevant, human‑readable information that you control.
For this specific case, Auth0Provider does not own the AuthenticationController class, so we should not add or change toString() there. The simplest, behaviour‑preserving fix is to remove the explicit toString() call and avoid depending on AuthenticationController’s string representation. Since the message already clearly states what happened (“Created AuthenticationController with MCD DomainResolver”), we can just log that without appending the controller instance. This preserves existing functionality (the controller is still created and returned; only the log format changes) and removes reliance on the default toString().
Concretely:
- In
src/main/java/com/auth0/test/Auth0Provider.java, line 26, replaceSystem.out.println("Created AuthenticationController with MCD DomainResolver "+controller.toString());withSystem.out.println("Created AuthenticationController with MCD DomainResolver");. - No additional methods, imports, or definitions are required.
-
Copy modified line R26
| @@ -23,7 +23,7 @@ | ||
| "<CLIENT_SECRET>") | ||
| .build(); | ||
|
|
||
| System.out.println("Created AuthenticationController with MCD DomainResolver "+controller.toString()); | ||
| System.out.println("Created AuthenticationController with MCD DomainResolver"); | ||
|
|
||
| } | ||
| return controller; |
| } | ||
|
|
||
| // Apply deferred settings | ||
| client.setLoggingEnabled(loggingEnabled); |
Check notice
Code scanning / CodeQL
Deprecated method or constructor invocation Note
AuthAPI.setLoggingEnabled
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
In general, the fix is to stop calling the deprecated AuthAPI.setLoggingEnabled(boolean) API and instead configure logging using the currently supported mechanism. In recent Auth0 Java SDKs, HTTP logging is controlled via HttpOptions (for example, by supplying a custom logging implementation) rather than a boolean flag on AuthAPI. In this code, RequestProcessor already has an HttpOptions httpOptions field that is passed into the AuthAPI constructor when non‑null, so the best way to preserve behavior without using the deprecated method is to handle the logging flag via HttpOptions before constructing the client, and stop calling setLoggingEnabled on the AuthAPI instance.
Because we must not change behavior outside the shown snippet and cannot modify other files, the minimal and safest concrete change here is to remove the call to the deprecated method and leave a clear comment indicating that logging should be configured through HttpOptions (which this class already supports), or elsewhere in the codebase. This avoids the deprecated API usage without affecting how the rest of this method works (telemetry, dynamic domain handling, etc.). Specifically, in src/main/java/com/auth0/RequestProcessor.java, in the createClientForDomain(String domain) method, remove line 211 (client.setLoggingEnabled(loggingEnabled);) and optionally add a brief comment explaining that logging configuration should be handled via HttpOptions or other up‑to‑date mechanisms rather than AuthAPI.setLoggingEnabled.
-
Copy modified line R210
| @@ -207,8 +207,7 @@ | ||
| client = new AuthAPI(domain, clientId, clientSecret); | ||
| } | ||
|
|
||
| // Apply deferred settings | ||
| client.setLoggingEnabled(loggingEnabled); | ||
| // Apply deferred settings (logging should be configured via HttpOptions or other supported mechanisms) | ||
| if (telemetryDisabled) { | ||
| client.doNotSendTelemetry(); | ||
| } else { |
Changes
Please describe both what is changing and why this is important. Include:
References
Please include relevant links supporting this change such as a:
Testing
Please describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this library has unit and/or integration testing, tests should be added for new functionality and existing tests should complete without errors.
Checklist