I'm working on implementing an authorization code flow in a Spring Boot application. The flow starts when a user submits a form with certain parameters. My handler for the /submit endpoint constructs an authorization code flow query string, including a state parameter. This state is an AES-encrypted JSON, which I also set as a cookie. After setting the cookie, the user is redirected to the /authorize endpoint.
However, I'm encountering an issue at the /callback endpoint: the originalState value is coming back as null. As I'm more accustomed to working with APIs rather than traditional MVC, I'm unsure how to tackle this problem.
Here's the relevant part of my code:
@Controller
public class MyController {
// dependencies
// ...
@PostMapping("/submit")
public ModelAndView processForm(
@RequestParam String formParam1,
@RequestParam String formaParam2
) throws Exception {
String stateJson = userInfoService.generateStateJson(formParam1, formParam2);
String encryptedStateJson = encryptService.encrypt(stateJson);
String state = Base64.getUrlEncoder().encodeToString(encryptedStateJson.getBytes(StandardCharsets.UTF_8));
ResponseCookie cookie = ResponseCookie.from("my-state", state)
.httpOnly(true)
.secure(true)
.path("/")
.build();
String redirectUri = UriComponentsBuilder
.fromUriString(appConfig.getAuthEndpoint())
.queryParam("client_id", appConfig.getClientId())
.queryParam("domain", appConfig.getDomain())
.queryParam("response_type", "code")
.queryParam("redirect_uri", appConfig.getRedirectUri())
.queryParam("state", state)
.queryParam("scope", appConfig.getScope())
.queryParam("prompt", "login")
.toUriString();
log.info("REDIRECT URI: {}", redirectUri);
log.info("STATE JSON: {}", stateJson);
log.info("STATE BASE64: {}", state);
RedirectView redirectView = new RedirectView(redirectUri);
redirectView.addStaticAttribute("Set-Cookie", cookie.toString());
return new ModelAndView(redirectView);
}
@GetMapping("/callback")
public String handleCallback(@RequestParam(name = "code") String code,
@RequestParam(name = "state") String receivedState,
@CookieValue(name = "my-state") String originalState, // <-- originalState is null here
Model model) {
log.info("ORIGINAL STATE: " + originalState);
log.info("RECEIVED STATE: " + receivedState);
if (originalState == null || !originalState.equals(receivedState)) {
model.addAttribute("error", "Error validating state.");
return "error";
}
// etc...
return "result";
}
}
I have a couple of questions:
Is my approach correct for ensuring a stateless operation, especially considering scenarios where the submit action might occur on one pod and the /callback on another? Do you have any suggestions for improvement? My goal is to maintain a stateless server environment. Thank you for your insights!
I attempted to verify if a cookie was successfully set in my Spring Boot application using the Chrome Debugger. My expectation was to see the cookie, which I set during the redirect process in my /submit handler, appear in the Chrome Debugger's cookie section. This cookie is crucial for maintaining a stateless operation in my authorization code flow, as it carries the state parameter used for validation in the /callback endpoint.
To check this, I navigated to the Application tab in Chrome's Developer Tools after triggering the redirect from the /submit endpoint. I expected to find the cookie listed under the Cookies section in the Storage area. However, I couldn't locate the cookie there. This absence is puzzling, and I'm not entirely sure if I'm checking in the correct place or at the right moment in the flow, especially considering that the user is redirected to an external IDP (Identity Provider) URL and then back to my application's /callback endpoint.
I'm seeking guidance on whether I'm following the correct procedure to verify the cookie's presence and any insights into why the cookie might not be appearing as expected.