I created a Spring Authorization Server and I’m trying to use it to fake Auth0 for Integration Tests and Local Development. I’ve had absolutely no luck getting the http coordination working. Currently I think that the problem is that /authorize is looking for an authenticated client, but authentication shouldn’t be needed at /authorize in pkce.
I’m not certain my server is actually configured properly at this moment, but here it is.
package com.xenoterracide.test.authorization.server;
import com.xenoterracide.tools.java.annotation.ExcludeFromGeneratedCoverageReport;
import java.util.UUID;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
/**
* Test Authorization Server to mimick Auth0.
*/
@SpringBootApplication(proxyBeanMethods = false)
public class AuthorizationServer {
public static final String CLIENT_ID = "client";
public static final String REDIRECT_URI = "http://localhost:3000";
AuthorizationServer() {}
/**
* Main.
*
* @param args arguments to the program
*/
@ExcludeFromGeneratedCoverageReport
public static void main(String[] args) {
SpringApplication.run(AuthorizationServer.class, args);
}
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
http
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling(
exceptions ->
exceptions.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
)
// Accept access tokens for User Info and/or Client Registration
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.cors(Customizer.withDefaults()).build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize.requestMatchers("/authorize").permitAll())
.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());
return http.cors(Customizer.withDefaults()).build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.addAllowedOrigin(REDIRECT_URI);
config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
RegisteredClientRepository registeredClientRepository() {
var publicClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId(CLIENT_ID)
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri(REDIRECT_URI)
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope(OidcScopes.EMAIL)
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(true).build())
.build();
return new InMemoryRegisteredClientRepository(publicClient);
}
}
spring.security.user.name = user
spring.security.user.password = pass
# match auth0
spring.security.oauth2.authorizationserver.endpoint.authorization-uri = /oauth/authorize
spring.security.oauth2.authorizationserver.endpoint.token-uri = /oauth/token
Specifically I’m trying to figure out the request I need for authorize
-> login
-> token
.
I’d prefer the answer be in “raw” http, or the output of curl verbose/httpcomponents logging would do. I don’t want java for that because then I’d have to interpret the java client.