oktadev / okta-spring-webclient-example

1 stars 2 forks source link

Infinite loop while doing simple login #2

Open anydoby opened 2 years ago

anydoby commented 2 years ago

I have tried to implement okta login in my webflux application and had issues. Having searched for the problem solution I found your repository and reduced it to just a helloworld endpoint that just returns a string to the authenticated user. Like this:

@RestController
public class SearchController {

    @GetMapping(value = "/hello")
    public Mono<String> getSearchCountAuthorized() {
        return Mono.just("hello");
    }

}

I put my development data to the properties file and launched the application. I get correctly redirected to okta login page, login and get redirected back, and then browser enters an infinite loop of logins and fails with too many redirects.

image

anydoby commented 2 years ago

I tried other examples from the okta site ones that use just springboot and mvc. They do work properly. Issue is with the webflux.

bdemers commented 2 years ago

Hi @anydoby!

Most of the time this error is caused by a configuration issue, like the client-secret is incorrect, or there is a network proxy/firewall issue.

What happens is:

  1. Your Spring application isn't authenticated so it redirects back to the IdP (Okta)
  2. The user authenticates with Okta
  3. Okta redirects back to your application "callback" route
  4. Your application makes a backchannel request to Okta (this is where any proxy/firewall would be seen), or if the client-secret is invalid
  5. If step "4" didn't complete successfully, your application is still in an unauthenticated state and will attempt to redirect back to the IdP.
  6. The user has already been authenticated with the IdP, so the IdP will automatically be redirect the user back to your application.
  7. GOTO: "4" (and get into this redirect loop)

There are a couple of other potential issues, but in all cases, I typically recommend turning up the logging. By default, Spring Security's logging is intentionally sparse (authentication errors happen and logging each one could be seen as log spam).

Tweak the log level with: logging.level.org.springframework.security=DEBUG

Keep us posted!

anydoby commented 2 years ago

Hi Brian,

Thanks a lot for taking time to look at my issue. I made sure I have correct secrets set everywhere :) And also prepared 2 simple projects that showcase the issue. First project is a copy of you sample with just a http://localhost:8080/hello endpoint that needs login (Webflux). Second project is the same, but I'm using spring MVC. It works. First one does loop indefinitely.

From what I see in the logs, the first project goes like this:

Received [GET /hello HTTP/1.1
Host: localhost:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Cookie: JSESSIONID=4845D7552F0FF651812C1BA231767D9B; 
....
Securing GET /hello
Set SecurityContextHolder to empty SecurityContext
Set SecurityContextHolder to anonymous SecurityContext
Request requested invalid session id 4845D7552F0FF651812C1BA231767D9B
Failed to authorize filter invocation [GET /hello] with attributes [authenticated]
Redirecting to http://localhost:8080/oauth2/authorization/okta
Received [GET /oauth2/authorization/okta HTTP/1.1
Cookie: JSESSIONID=54D25F9DA2EC88B99A43CF5968E6CB34 <--- new session id
Redirecting to https://dev-94528537.okta.com/oauth2/default/v1/authorize?response_type=code&client_id=0oa3fliep9eL6NQbL5d7&scope=openid%20profile%20email&state=CaX_WWgcRD-OBBhihFDrZZp2YPsIgcsTxcTfS6p3mEk%3D&redirect_uri=http://localhost:8080/authorization-code/callback&nonce=erHExGKnXb2APkQtzVHWwdrY6IkaR7WWuSfJ4fWLkEI
Received [GET /authorization-code/callback?code=7YymcY9AhxTph_EnOAQrJvs-nGCInaoZ-lwugsJdZ6U&state=CaX_WWgcRD-OBBhihFDrZZp2YPsIgcsTxcTfS6p3mEk%3D HTTP/1.1
Host: localhost:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Cookie: JSESSIONID=54D25F9DA2EC88B99A43CF5968E6CB34
Securing GET /authorization-code/callback?code=7YymcY9AhxTph_EnOAQrJvs-nGCInaoZ-lwugsJdZ6U&state=CaX_WWgcRD-OBBhihFDrZZp2YPsIgcsTxcTfS6p3mEk%3D
Start processing with input [code=7YymcY9AhxTph_EnOAQrJvs-nGCInaoZ-lwugsJdZ6U&state=CaX_WWgcRD-OBBhihFDrZZp2YPsIgcsTxcTfS6p3mEk%3D]
HTTP POST https://dev-94528537.okta.com/oauth2/default/v1/token

So after login we get the callback and then call okta to create token, keys, userinfo blabla. Works great.

In the case of a webflux project the log is simpler:

[03b5fde9-1, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:56938] HTTP GET "/hello"
Request 'GET /hello' doesn't match 'null /oauth2/authorization/{registrationId}'
Request 'GET /hello' doesn't match 'null /login/oauth2/code/{registrationId}'
Created new WebSession.
Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/logout', method=POST}
Request 'GET /hello' doesn't match 'POST /logout'
No matches found
... trying to authorize....
Redirecting to '/oauth2/authorization/okta'
[03b5fde9-2, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:56938] HTTP GET "/oauth2/authorization/okta"
Checking match of request : '/oauth2/authorization/okta'; against '/oauth2/authorization/{registrationId}'
Redirecting to 'https://dev-94528537.okta.com/oauth2/default/v1/authorize?response_type=code&client_id=0oa3fliep9eL6NQbL5d7&scope=openid%20profile%20email&state=xxA3mvFdNuyp6iNbPkT06YW6HGJw3hDPu4SnS-MVlD8%3D&redirect_uri=http://localhost:8080/authorization-code/callback&nonce=xEUJPpRY6Vzu1Riy8KCKgJHasPvyqC4j9O8zz1HSuxg'
[03b5fde9-3, L:/[0:0:0:0:0:0:0:1]:8080 - R:/[0:0:0:0:0:0:0:1]:56938] HTTP GET "/authorization-code/callback?code=nlXa46L2CO0uD_bkR25YVH7q51NyICq46cvCK7KBLEs&state=xxA3mvFdNuyp6iNbPkT06YW6HGJw3hDPu4SnS-MVlD8%3D"
Request 'GET /authorization-code/callback' doesn't match 'null /oauth2/authorization/{registrationId}'
Request 'GET /authorization-code/callback' doesn't match 'null /login/oauth2/code/{registrationId}'
Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/logout', method=POST}
Request 'GET /authorization-code/callback' doesn't match 'POST /logout'
No matches found
Checking authorization on '/authorization-code/callback' using org.springframework.security.authorization.AuthenticatedReactiveAuthorizationManager@50924a87
No SecurityContext found in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession@5390b8ba'
Authorization failed: Access Denied
No SecurityContext found in WebSession: 'org.springframework.web.server.session.InMemoryWebSessionStore$InMemoryWebSession@5390b8ba'
Trying to match using OrServerWebExchangeMatcher{matchers=[PathMatcherServerWebExchangeMatcher{pattern='/**', method=GET}]}
Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/**', method=GET}
Checking match of request : '/authorization-code/callback'; against '/**'
matched
Trying to match using NegatedServerWebExchangeMatcher{matcher=OrServerWebExchangeMatcher{matchers=[PathMatcherServerWebExchangeMatcher{pattern='/favicon.*', method=null}]}}
Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/favicon.*', method=null}
Request 'GET /authorization-code/callback' doesn't match 'null /favicon.*'
No matches found
matches = true
Trying to match using MediaTypeRequestMatcher [matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[*/*]]
httpRequestMediaTypes=[text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8]
Processing text/html
text/html .isCompatibleWith text/html = true
All requestMatchers returned true
Request added to WebSession: '/authorization-code/callback?code=nlXa46L2CO0uD_bkR25YVH7q51NyICq46cvCK7KBLEs&state=xxA3mvFdNuyp6iNbPkT06YW6HGJw3hDPu4SnS-MVlD8%3D'
Redirecting to '/oauth2/authorization/okta'

If feels like we're getting the callback but there's not handler for the /authorization-code/callback.

anydoby commented 2 years ago

So the non-working example is here https://github.com/anydoby/okta-webflux (with all the necessary keys for testing, just hit http://localhost:8080/hello), creds are test@gmail.com/***** A working example with simple spring web is here: https://github.com/anydoby/okta-springmvc

bdemers commented 2 years ago

@anydoby That helped! I was able to reproduce this issue. A few things:

  1. First delete the application and user mentioned in the above comment

  2. Use the default callback uri instead of setting a custom one. It looks like you have conflicting settings

    Delete these lines: https://github.com/anydoby/okta-webflux/blob/4d54d043663d6fff6bd04acc9b4bb7b0fe8f47dc/src/main/java/com/okta/developer/search/security/SecurityConfiguration.java#L24

    And these: https://github.com/anydoby/okta-webflux/blob/4d54d043663d6fff6bd04acc9b4bb7b0fe8f47dc/src/main/resources/application.yml#L7

    Update your Okta OAuth application to use the default Spring callback uri: /login/oauth2/code/okta

  3. It looks like there might be an issue with WebFlux and setting custom redirect-uris, I've opened an issue https://github.com/okta/okta-spring-boot/issues/388 for us to look into this more.

anydoby commented 2 years ago

Great, your suggestion works with webflux.

arvindkrishnakumar-okta commented 2 years ago

@anydoby glad to hear that worked!

@bdemers I've opened an issue with spring-security and will stay tuned.

arvindkrishnakumar-okta commented 2 years ago

@anydoby: Spring Sec team pointed me to the latest doc at https://docs.spring.io/spring-security/reference/reactive/oauth2/login/advanced.html#webflux-oauth2-login-advanced-redirection-endpoint. This shows how to set a custom redirect-uri programmatically.

jozard commented 2 years ago

@bdemers do I understand correctly that if I have to use a custom redirect-uri, there is no solution yet?