Search code examples
spring-authorization-server

Can a user login without a client-id as in the test case in Spring Authorization Server sample project?


I am curious about this login step in the test case in Spring Authorization Server defaultAuthorizationServer sample project test case that does not have a client-id associated but the user can still login:

@Test
    public void whenLoggingInAndRequestingTokenThenRedirectsToClientApplication() throws IOException {
        // Log in
        this.webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
        this.webClient.getOptions().setRedirectEnabled(false);
        signIn(this.webClient.getPage("/login"), "user1", "password");

        // Request token
        WebResponse response = this.webClient.getPage(AUTHORIZATION_REQUEST).getWebResponse();

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.MOVED_PERMANENTLY.value());
        String location = response.getResponseHeaderValue("location");
        assertThat(location).startsWith(REDIRECT_URI);
        assertThat(location).contains("code=");
    }

I ran this test case in my project. I have a AuthenticationProvider implementation and I checked for the clientId and it is null based on the RequestCache. Will there be a case where the user can login to Authorization server without a client id? My thoughts are that there will always be a client app or client-id associated when a user logs in to the login page.

The following is my AuthenticationProvider implementation:

@Service
public class AuthenticationCallout implements AuthenticationProvider {
    private static final Logger LOG = LoggerFactory.getLogger(AuthenticationCallout.class);

    private RequestCache requestCache;
    private WebClient.Builder webClientBuilder;


    public AuthenticationCallout(WebClient.Builder webClientBuilder, RequestCache requestCache) {
        this.webClientBuilder = webClientBuilder;
        this.requestCache = requestCache;
    }


    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        LOG.info("authenticate with username and password");

        final String clientId = ClientIdUtil.getClientId(requestCache);
        LOG.info("clientId: {}", clientId);  //client-id is always null because the login does not have a client association
...
}

//ClientIdUtil implmentation
public class ClientIdUtil {
    private static final Logger LOG = LoggerFactory.getLogger(ClientIdUtil.class);

    public static String getClientId(RequestCache requestCache) {
        var requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        var request = requestAttributes.getRequest();
        var response = requestAttributes.getResponse();
        var savedRequest = requestCache.getRequest(request, response);
        return ClientIdUtil.getParameter(savedRequest, OAuth2ParameterNames.CLIENT_ID);
    }
}

Solution

  • If the user initiates authorization from the client (via a redirect to log in or to authorize a client), there will be a request in the RequestCache. However, the authorization server is still just a normal application, with its own login. So a user can (and in more advanced authorization server deployments, will) log directly into the authorization server.

    Imagine a scenario where the user forgot their password, so they click "Forgot Password" on a desktop, and have an email sent to them. They click a link in the email on their phone to reset the password, and log in successfully on their phone afterwards. There will be no request in the RequestCache. There are many other scenarios where a user would log in first (e.g. to update their profile, password, security options, visit a dashboard, etc.).

    In the test case, it is indeed the case that the user logs in first, and then goes through the authorization code flow.