I have a Spring Boot application that is bundled with React for the front-end.
React is a single-page application, served at http://localhost:8080/. It will take any page changes and update the local browser URL to look like it's changed to a new page, however, it still is actually at the root location. For example, clicking "Admin Dashboard" would update the browser URL to say "http://localhost:8080/admin" but it would still be on the main page (apologies for any over-explanation).
If a user clicks refresh though, the browser would ask the server for /admin which doesn't actually exist. To work around this I am redirecting any 404 errors back to the root URL (http://localhost:8080/) and React will intelligently display the Admin page, so it appears it's more than a single-page application.
This works fine, but my current issue is I'm trying to change the security so that my application becomes an OAuth 1.0a (the producer is an older app that only supports 1.0a) Consumer and authenticates the user against the 3rd Party OAuth Producer application, and if the credentials authenticate successfully, they are redirected back to my application and can proceed to access my applications protected resources (please let me know if my expectation of how this works is wrong though).
Problem: The application redirects requests to the 3rd-party application and I can login with no issues. However, I get stuck in an OAuth loop and it times out with ERR_TOO_MANY_REDIRECTS. I think it gets stuck in Step 3 where it's redirected to the application and the application should then exchange the details for an access token. I think whatever is handling Step 3 is being treated as a protected resource, so it keeps replaying the OAuth flow, but not sure though. But it doesn't make sense as all requests are permitted except the authorized resource (/static/testing/** - which is the only thing in that directory - changed it so I could be more sure of how things are laid out). Do I need to handle this final step with the code of my own?
Here is the final URL when the page times out with ERR_TOO_MANY_REDIRECTS. It has a lot of new instances of ?oauth_token=&oauth_verifier=. Each one has different keys, so it seems it's hitting something that's preventing it to complete the OAuth flow and keeps repeating the steps from the start, but getting stuck at the same place.
http://localhost:8080/static/testing/index.html?oauth_token=b20eecd7a7994a62b751b43458049302&oauth_verifier=c03877ca49be497fb7f2e803e97e8137&oauth_token=25097eab2e3b4b97bf6cbb5a112bb4a8&oauth_verifier=74877789499242f18a9b4d01ee9fea44&oauth_token=6d3136579d234b1c8d21c5f1aabe43a1&oauth_verifier=fe51e99d83954f928b598923ff10fc44&oauth_token=dcb33bce0d8b40d89ae09429adc0fd73&oauth_verifier=4df25da7f1b1403880dce4f7d36b25a5&oauth_token=e9cb8a40323448bbad391df137b7e81d&oauth_verifier=885aa185049f426895529885f34f85c6&oauth_token=bcbe5012553e405c8b6699907fd78256&oauth_verifier=ee9c69c3d1df433289562008b5483810&oauth_token=d7fb00125b664bff820488dc4bc1852d&oauth_verifier=b0c7dee12f4647f2821860170b1c6c42&oauth_token=fbb0f9a6075345c6b1e2c1f11fc874a6&oauth_verifier=f30761f460134103a132c60803145358&oauth_token=ff5da5a77b2e4599b3b3465b48668504&oauth_verifier=a9fe7da1abb1460a8836896aaa442cc9&oauth_token=726ea4b12fd8445bb2cb5e1de10fd94b&oauth_verifier=8d8653ea2dbe4547a0401c6b8524638b&oauth_token=6b8e507ce3254f79b8ce6d98c9ec6784&oauth_verifier=491e101a3cc04c01b16e8834711a0ac8&oauth_token=792be2983ca1496a9ec0f7384eae6316&oauth_verifier=db916051bff443ceacd1b59d2d87cdb8&oauth_token=98f1d137d7144e568f96b7b21df0679c&oauth_verifier=06c3c963161d4fcd966573db262d021c&oauth_token=74ef3c374186405a93a507ebfd7779f0&oauth_verifier=9e492fcf456d4c4e80cb21ed5ec9c6e5&oauth_token=6d609503d8c64cca993ab9298a85054e&oauth_verifier=41e4aca57f184c7b88f4d82820c1f5ab&oauth_token=0f32bfceaa474bff91a88dafe4da9d66&oauth_verifier=59a67e101fe64ee7ba7531d8cb7f65af&oauth_token=af4065dac8184b748d8719cc2f2d429d&oauth_verifier=20d0ffee40214635a12f987b7d338053&oauth_token=718f65634195446c831b8acc9d9ddc81&oauth_verifier=3e6b0ac42028403abf0ed77d0b205fe7&oauth_token=6ce3d8e1460748ccb6a5a151cc467d40&oauth_verifier=dd5173ad4fab491aab50a66047329dfa
Note: Here are the redirects in my application (not directly related, but could be so wanted to provide context)
Redirects:
*RedirectController*: /private to / <-- was used in the original login implementation
*NotFoundHandler*: 404 to /static/index.html
*WebSocketConfig*: Registered at /ws
*NotFoundHandler Source Code*: (and we removed application.getSources().remove(ErrorPageFilter.class) from ApplicationContext)
@ControllerAdvice
public class NotFoundHandler {
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<String> renderDefaultPage() {
try {
InputStream inputStream = new ClassPathResource("/static/index.html").getInputStream();
String body = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(body);
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("There was an error completing the action.");
}
}
}
OAuth Configuration: Note: I've allowed all requests without authentication (/), except one directory (/static/testing/) - or that is my intention at least.
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// permit all requests (except the /static/testing/** which is called out further down)
http.authorizeRequests().antMatchers("/**").permitAll();
http.addFilterAfter(this.oauthConsumerContextFilter(), SwitchUserFilter.class);
http.addFilterAfter(this.oauthConsumerProcessingFilter(), OAuthConsumerContextFilter.class);
}
// IMPORTANT: this must not be a Bean
OAuthConsumerContextFilter oauthConsumerContextFilter() {
OAuthConsumerContextFilter filter = new OAuthConsumerContextFilter();
filter.setConsumerSupport(this.consumerSupport());
return filter;
}
// IMPORTANT: this must not be a Bean
OAuthConsumerProcessingFilter oauthConsumerProcessingFilter() {
OAuthConsumerProcessingFilter filter = new OAuthConsumerProcessingFilter();
filter.setProtectedResourceDetailsService(this.prds());
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = new LinkedHashMap<>();
// one entry per oauth:url element in xml
map.put(
// 1st arg is equivalent of url:pattern in xml
// 2nd arg is equivalent of url:httpMethod in xml
new AntPathRequestMatcher("/static/testing/**", null),
// arg is equivalent of url:resources in xml
// IMPORTANT: this must match the ids in prds() and prd() below
Collections.singletonList(new SecurityConfig("myResource")));
filter.setObjectDefinitionSource(new DefaultFilterInvocationSecurityMetadataSource(map));
return filter;
}
@Bean // optional, I re-use it elsewhere, hence the Bean
OAuthConsumerSupport consumerSupport() {
CoreOAuthConsumerSupport consumerSupport = new CoreOAuthConsumerSupport();
consumerSupport.setProtectedResourceDetailsService(prds());
return consumerSupport;
}
@Bean // optional, I re-use it elsewhere, hence the Bean
ProtectedResourceDetailsService prds() {
return (String id) -> {
switch (id) {
// this must match the id in prd() below
case "myResource":
return prd();
}
throw new RuntimeException("Invalid id: " + id);
};
}
ProtectedResourceDetails prd() {
BaseProtectedResourceDetails details = new BaseProtectedResourceDetails();
// this must be present and match the id in prds() and prd() above
details.setId("myResource");
details.setConsumerKey("<consumer key was here>");
details.setSharedSecret(new SharedConsumerSecretImpl("<consumer secret was here>"));
details.setRequestTokenURL("https://localhost:9443/oauth-request-token");
details.setUserAuthorizationURL("https://localhost:9443/oauth-authorize");
details.setAccessTokenURL("https://localhost:9443/oauth-access-token");
// any other service-specific settings
return details;
}
}
Final Question It seems to get stuck at the final major step of the OAuth flow, after it receives the oauth_token and oauth_verifier from the Producer, and never makes the final request to get an access token using the access token URL. Does anyone know why it would be getting stuck at this point?
Is that in the final step it redirects back to the protected resource (/static/testing/index.html), and the oauthConsumerProcessingFilter isn't intercepting the request first and requesting the access token?
Or is that I should have been forwarding /static/testing/index.html to the Provider instead of serving local content, and my understanding of the OAuth flow won't work out of the box for the use case I'm trying to do (really just use OAuth for user verification against the Provider when my own applications protected resources are being accessed)?
###################### EDIT ######################
Adding Spring Log:
Here is the Spring log that shows the initial token request from the Provider, the redirection to the Provider, but once the Provider is logged into and redirects back to the Callback, Spring doesn't recognize the current oauth process in progress, and starts it over again with the oauth_token and oauth_verifier appended to the URL, and loops this process indefinitely til the browser fails with ERR_TOO_MANY_REDIRECTS.
2020-08-12T14:53:56,383 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-08-12T14:53:56,383 DEBUG http-nio-8080-exec-1 o.s.s.w.c.HttpSessionSecurityContextRepository: No HttpSession currently exists
2020-08-12T14:53:56,383 DEBUG http-nio-8080-exec-1 o.s.s.w.c.HttpSessionSecurityContextRepository: No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-08-12T14:53:56,385 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-08-12T14:53:56,385 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 5 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.u.m.AntPathRequestMatcher: Request 'GET /static/testing/index.html' doesn't match 'POST /logout'
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 6 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.s.HttpSessionRequestCache: saved request doesn't match
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 7 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-08-12T14:53:56,387 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 8 of 13 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2020-08-12T14:53:56,388 DEBUG http-nio-8080-exec-1 o.s.s.w.a.AnonymousAuthenticationFilter: Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@7bf9dbd5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2020-08-12T14:53:56,388 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 9 of 13 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-08-12T14:53:56,388 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 10 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-08-12T14:53:56,389 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 11 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-08-12T14:53:56,389 DEBUG http-nio-8080-exec-1 o.s.s.w.u.m.AntPathRequestMatcher: Request '/static/testing/index.html' matched by universal pattern '/**' (it matches against the /** permit All)
2020-08-12T14:53:56,389 DEBUG http-nio-8080-exec-1 o.s.s.a.i.AbstractSecurityInterceptor: Secure object: FilterInvocation: URL: /static/testing/index.html; Attributes: [permitAll]
2020-08-12T14:53:56,389 DEBUG http-nio-8080-exec-1 o.s.s.a.i.AbstractSecurityInterceptor: Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@7bf9dbd5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2020-08-12T14:53:56,393 DEBUG http-nio-8080-exec-1 o.s.s.a.v.AffirmativeBased: Voter: org.springframework.security.web.access.expression.WebExpressionVoter@2987f971, returned: 1
2020-08-12T14:53:56,393 DEBUG http-nio-8080-exec-1 o.s.s.a.i.AbstractSecurityInterceptor: Authorization successful
2020-08-12T14:53:56,393 DEBUG http-nio-8080-exec-1 o.s.s.a.i.AbstractSecurityInterceptor: RunAsManager did not change Authentication object
2020-08-12T14:53:56,393 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 12 of 13 in additional filter chain; firing Filter: 'OAuthConsumerContextFilter'
2020-08-12T14:53:56,394 DEBUG http-nio-8080-exec-1 o.s.s.o.c.f.OAuthConsumerContextFilter: Storing access tokens in request attribute 'OAUTH_ACCESS_TOKENS'.
2020-08-12T14:53:56,394 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 13 of 13 in additional filter chain; firing Filter: 'OAuthConsumerProcessingFilter'
2020-08-12T14:53:56,394 DEBUG http-nio-8080-exec-1 o.s.s.w.u.m.AntPathRequestMatcher: Checking match of request : '/static/testing/index.html'; against '/static/testing/**' (it further matches against the OAuth resource since it is /static/testing/**)
2020-08-12T14:53:56,404 DEBUG http-nio-8080-exec-1 o.s.s.o.c.f.OAuthConsumerContextFilter: Obtaining request token for resource: myResource (Step 1a: Getting request token from JTS. The callback URL is the protected page that we requested, is that a problem? Or should a filter intercept it on the next step to take over part 3 in the process before actually requesting the resource?)
2020-08-12T14:53:56,548 DEBUG http-nio-8080-exec-1 o.s.s.o.c.s.HMAC_SHA1SignatureMethod: signature base: POST&https%3A%2F%2Flocalhost%3A9443%2Fjts%2Foauth-request-token&oauth_callback%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fstatic%252Ftesting%252Findex.html%26oauth_consumer_key%3D14b5f4e56856464c9ba64e11a6d558cb%26oauth_nonce%3Dce7d30a7-0b11-4525-a42f-9a353868a5f2%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1597258436%26oauth_version%3D1.0
2020-08-12T14:53:56,548 DEBUG http-nio-8080-exec-1 o.s.s.o.c.s.HMAC_SHA1SignatureMethod: signature: zAri6vOmcybjeKi3W6CvTkMv5uk=
2020-08-12T14:53:56,628 DEBUG http-nio-8080-exec-1 o.s.s.o.c.f.OAuthConsumerContextFilter: Request token obtained for resource myResource: cba433cd39a34a34a80e3dbe96d593bd (Step 1b:Received the request token from JTS)
2020-08-12T14:53:56,629 DEBUG http-nio-8080-exec-1 o.s.s.o.c.f.OAuthConsumerContextFilter: Redirecting request to https://localhost:9443/jts/oauth-authorize?oauth_token=cba433cd39a34a34a80e3dbe96d593bd for user authorization of the request token for resource myResource. (Step 2a: redirecting user to the JTS login page for authorization)
2020-08-12T14:53:56,629 DEBUG http-nio-8080-exec-1 o.s.s.w.DefaultRedirectStrategy: Redirecting to 'https://localhost:9443/jts/oauth-authorize?oauth_token=cba433cd39a34a34a80e3dbe96d593bd'
2020-08-12T14:53:56,630 DEBUG http-nio-8080-exec-1 o.s.s.w.h.w.HstsHeaderWriter: Not injecting HSTS header since it did not match the requestMatcher (is this line and the next 3 a problem? Should it be storing the security context and can’t because of the http/https mismatch?)org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@3df79515
2020-08-12T14:53:56,630 DEBUG http-nio-8080-exec-1 o.s.s.w.c.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper: SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-08-12T14:53:56,631 DEBUG http-nio-8080-exec-1 o.s.s.w.a.ExceptionTranslationFilter: Chain processed normally
2020-08-12T14:53:56,632 DEBUG http-nio-8080-exec-1 o.s.s.w.c.SecurityContextPersistenceFilter: SecurityContextHolder now cleared, as request processing completed
2020-08-12T14:53:59,846 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html? (We’ve now received the call back redirection from JTS to go back to /static/testing/index.html but with the oauth_token and oauth_verifier which would be used in the final step, sending those back to JTS to get a oauth_access token (using /jts/oauth-access-token). However, it seems it doesn’t realize it has the current OAuth flow in process, and repeats all of the above steps in an infinite loop til the browser stops the process with ERR_TOO_MANY_REDIRECTS. Is this because the SecurityContext wasn’t stored? Or another configuration issue I’m missing?). oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-08-12T14:53:59,846 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-08-12T14:53:59,846 DEBUG http-nio-8080-exec-2 o.s.s.w.c.HttpSessionSecurityContextRepository: No HttpSession currently exists
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.c.HttpSessionSecurityContextRepository: No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 5 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.u.m.AntPathRequestMatcher: Request 'GET /static/testing/index.html' doesn't match 'POST /logout'
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 6 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.s.HttpSessionRequestCache: saved request doesn't match
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 7 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 8 of 13 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.a.AnonymousAuthenticationFilter: Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@7bf9dbd5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 9 of 13 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 10 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 11 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.w.u.m.AntPathRequestMatcher: Request '/static/testing/index.html' matched by universal pattern '/**'
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.a.i.AbstractSecurityInterceptor: Secure object: FilterInvocation: URL: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca; Attributes: [permitAll]
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.a.i.AbstractSecurityInterceptor: Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@7bf9dbd5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2020-08-12T14:53:59,850 DEBUG http-nio-8080-exec-2 o.s.s.a.v.AffirmativeBased: Voter: org.springframework.security.web.access.expression.WebExpressionVoter@2987f971, returned: 1
2020-08-12T14:53:59,850 DEBUG http-nio-8080-exec-2 o.s.s.a.i.AbstractSecurityInterceptor: Authorization successful
2020-08-12T14:53:59,850 DEBUG http-nio-8080-exec-2 o.s.s.a.i.AbstractSecurityInterceptor: RunAsManager did not change Authentication object
2020-08-12T14:53:59,850 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 12 of 13 in additional filter chain; firing Filter: 'OAuthConsumerContextFilter'
2020-08-12T14:53:59,851 DEBUG http-nio-8080-exec-2 o.s.s.o.c.f.OAuthConsumerContextFilter: Storing access tokens in request attribute 'OAUTH_ACCESS_TOKENS'.
2020-08-12T14:53:59,851 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 13 of 13 in additional filter chain; firing Filter: 'OAuthConsumerProcessingFilter'
2020-08-12T14:53:59,851 DEBUG http-nio-8080-exec-2 o.s.s.w.u.m.AntPathRequestMatcher: Checking match of request : '/static/testing/index.html'; against '/static/testing/**'
2020-08-12T14:53:59,852 DEBUG http-nio-8080-exec-2 o.s.s.o.c.f.OAuthConsumerContextFilter: Obtaining request token for resource: myResource
2020-08-12T14:53:59,853 DEBUG http-nio-8080-exec-2 o.s.s.o.c.s.HMAC_SHA1SignatureMethod: signature base: POST&https%3A%2F%2Flocalhost%3A9443%2Fjts%2Foauth-request-token&oauth_callback%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fstatic%252Ftesting%252Findex.html%253Foauth_token%253Dcba433cd39a34a34a80e3dbe96d593bd%2526oauth_verifier%253D82d017779d1547129e84b3173e7c3dca%26oauth_consumer_key%3D14b5f4e56856464c9ba64e11a6d558cb%26oauth_nonce%3D08415a86-c1b8-4bcf-9379-961afddd6f3e%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1597258439%26oauth_version%3D1.0
2020-08-12T14:53:59,853 DEBUG http-nio-8080-exec-2 o.s.s.o.c.s.HMAC_SHA1SignatureMethod: signature: x/yPyZhL1RS8RqDOpqnpBwjG57I=
2020-08-12T14:53:59,856 DEBUG http-nio-8080-exec-2 o.s.s.o.c.f.OAuthConsumerContextFilter: Request token obtained for resource myResource: c97af51ba3554c24a8a00642ca84d950
2020-08-12T14:53:59,856 DEBUG http-nio-8080-exec-2 o.s.s.o.c.f.OAuthConsumerContextFilter: Redirecting request to https://localhost:9443/jts/oauth-authorize?oauth_token=c97af51ba3554c24a8a00642ca84d950 for user authorization of the request token for resource myResource.
2020-08-12T14:53:59,856 DEBUG http-nio-8080-exec-2 o.s.s.w.DefaultRedirectStrategy: Redirecting to 'https://localhost:9443/jts/oauth-authorize?oauth_token=c97af51ba3554c24a8a00642ca84d950'
2020-08-12T14:53:59,857 DEBUG http-nio-8080-exec-2 o.s.s.w.h.w.HstsHeaderWriter: Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@3df79515
2020-08-12T14:53:59,857 DEBUG http-nio-8080-exec-2 o.s.s.w.c.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper: SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-08-12T14:53:59,857 DEBUG http-nio-8080-exec-2 o.s.s.w.a.ExceptionTranslationFilter: Chain processed normally
2020-08-12T14:53:59,858 DEBUG http-nio-8080-exec-2 o.s.s.w.c.SecurityContextPersistenceFilter: SecurityContextHolder now cleared, as request processing completed```
Not for sure what the error was, but once I moved the Provider to it's own host, domain, and proper SSL setup, it started working ok. (Previously both were on the local host and the Provider had an invalid SSL certificate, but I had imported it within the JRE).
Thanks @jzheaux!