Merge pull request #623 from gale-popai/fix/oauth-return-to

fix(auth): keep the OAuth return target through the provider callback
This commit is contained in:
XiaoSeS 2026-08-26 15:29:48 +08:00 committed by GitHub
commit 3522bad295
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 35 additions and 5 deletions

View file

@ -28,15 +28,26 @@ public class SkillHubOAuth2AuthorizationRequestResolver
@Override @Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
OAuth2AuthorizationRequest authorizationRequest = delegate.resolve(request); return rememberIfAuthorizationRequest(request, delegate.resolve(request));
oauthLoginFlowService.rememberReturnTo(request);
return authorizationRequest;
} }
@Override @Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) { public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
OAuth2AuthorizationRequest authorizationRequest = delegate.resolve(request, clientRegistrationId); return rememberIfAuthorizationRequest(request, delegate.resolve(request, clientRegistrationId));
oauthLoginFlowService.rememberReturnTo(request); }
/**
* {@code OAuth2AuthorizationRequestRedirectFilter} calls the resolver on every request in the
* chain, not only on authorization requests; the delegate simply answers null for the rest.
* Recording the return target on those calls would clear it again on the very next request
* including the provider callback, which carries no {@code returnTo} and is processed by this
* filter before authentication succeeds. Only an actual authorization request may touch it.
*/
private OAuth2AuthorizationRequest rememberIfAuthorizationRequest(
HttpServletRequest request, OAuth2AuthorizationRequest authorizationRequest) {
if (authorizationRequest != null) {
oauthLoginFlowService.rememberReturnTo(request);
}
return authorizationRequest; return authorizationRequest;
} }
} }

View file

@ -54,6 +54,25 @@ class OAuth2AuthorizationRequestResolverTest {
.isEqualTo("/dashboard/publish?draft=1"); .isEqualTo("/dashboard/publish?draft=1");
} }
@Test
void resolve_keepsReturnToOnNonAuthorizationRequests() {
// The redirect filter runs the resolver on every request in the chain, the provider
// callback included. That request carries no returnTo, so treating it as an
// authorization request would clear the target before the success handler reads it.
MockHttpServletRequest authorization = new MockHttpServletRequest("GET", "/oauth2/authorization/github");
authorization.setParameter("returnTo", "/device");
resolver.resolve(authorization, "github");
HttpSession session = authorization.getSession(false);
MockHttpServletRequest callback = new MockHttpServletRequest("GET", "/login/oauth2/code/github");
callback.setParameter("code", "auth-code");
callback.setSession(session);
assertThat(resolver.resolve(callback)).isNull();
assertThat(session.getAttribute(OAuthLoginRedirectSupport.SESSION_RETURN_TO_ATTRIBUTE))
.isEqualTo("/device");
}
@Test @Test
void resolve_ignoresUnsafeReturnTo() { void resolve_ignoresUnsafeReturnTo() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/oauth2/authorization/github"); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/oauth2/authorization/github");