Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT validation failed using Cognito with Google Oauth2 #346

Closed
dgbmariano opened this issue Aug 15, 2020 · 6 comments · Fixed by #355
Closed

JWT validation failed using Cognito with Google Oauth2 #346

dgbmariano opened this issue Aug 15, 2020 · 6 comments · Fixed by #355
Assignees
Labels
info: workaround available A workaround is available for the issue relates-to: oidc type: bug Something isn't working

Comments

@dgbmariano
Copy link

dgbmariano commented Aug 15, 2020

We are updating to Micronaut 2.0, but a problem occur with our current login workflow. We use cognito as our authentication provider and the login is made using Google.
When we try to sign in in the first time, we receive the following error:

{"message":"JWT validation failed","_links":{"self":{"href":"/oauth/callback/cognito?code=xxxxxx....","templated":false}}}

If we go back to the sign in page and try again the authentication proccess is successful.

Steps to Reproduce

  1. Clone this repo: https://github.com/dgbmariano/cognito-authentication-problem
  2. Follow this guide to configure Cognito in AWS: https://guides.micronaut.io/micronaut-oauth2-cognito/guide/index.html
  3. When modifying the app Settings, set this configurations:
    image
  4. You will need to create a new Google OAuth 2.0 Client:
    image
  5. Now replace the client-id, client-secret and the necessary fields in issuer in the application.yml.
  6. Run the application and open http://localhost:8080/
  7. Click in Enter
  8. After sign in with Google, the authentication will fail with the message:
{"message":"JWT validation failed","_links":{"self":{"href":"/oauth/callback/cognito?code=xxxxxx....","templated":false}}}
  1. Go back to http://localhost:8080/ and click in Enter again. The authentication will be successful now.

Expected Behaviour

The authentication should be successful in the first try.

Actual Behaviour

It fail once, then it authenticate.

Environment Information

  • Operating System: Ubuntu 20.02
  • Micronaut Version: 2.0.1
@sdelamo sdelamo self-assigned this Aug 15, 2020
@sdelamo sdelamo added this to To do in Micronaut Development via automation Aug 15, 2020
@sdelamo sdelamo moved this from To do to In progress in Micronaut Development Aug 17, 2020
@sdelamo
Copy link
Contributor

sdelamo commented Aug 17, 2020

The problem, the nonce is not being persisted and thus we are not able to validate it.

A workaround is to disable the nonce validator:

package io.micronaut.security.oauth2.endpoint.token.response.validation;

import edu.umd.cs.findbugs.annotations.Nullable;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.security.oauth2.client.OpenIdProviderMetadata;
import io.micronaut.security.oauth2.configuration.OauthClientConfiguration;
import io.micronaut.security.oauth2.endpoint.token.response.OpenIdClaims;

@Replaces(NonceClaimValidator.class)
public class NonceClaimValidatorReplacement extends NonceClaimValidator {

    @Override
    public boolean validate(OpenIdClaims claims,
                            OauthClientConfiguration clientConfiguration,
                            OpenIdProviderMetadata providerMetadata,
                            @Nullable String nonce) {
        return true;
    }
}

I will add several PRs to improve logs and ease override and fix this.

In your code, add <logger name="io.micronaut.security" level="TRACE"/> to logback.xml to get better visibility.

I also modified thymeleaf to get the cognito username because I am not getting an email back from gmail signin


<h2 th:if="${security}">username: <span th:text="${security.attributes.get('email')}"></span><span th:text="${security.attributes.get('cognito:username')}"></span></h2>
<h2 th:unless="${security}">username: Anonymous</h2>

@sdelamo sdelamo added closed: notabug The issue is not a bug info: workaround available A workaround is available for the issue labels Aug 17, 2020
sdelamo added a commit that referenced this issue Aug 17, 2020
@sdelamo
Copy link
Contributor

sdelamo commented Aug 17, 2020

We include a nonce. More info about nonce validation https://openid.net/specs/openid-connect-core-1_0.html#IDToken

String value used to associate a Client session with an ID Token, and to mitigate replay attacks.
The value is passed through unmodified from the Authentication Request to the ID Token.
If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request. If present in the Authentication Request, Authorization Servers MUST include a nonce Claim in the ID Token with the Claim Value being the nonce value sent in the Authentication Request. Authorization Servers SHOULD perform no other processing on nonce values used. The nonce value is a case sensitive string.

@sdelamo sdelamo added type: bug Something isn't working and removed closed: notabug The issue is not a bug labels Aug 17, 2020
@dgbmariano
Copy link
Author

Thanks for the workaround (and looking into the problem)!
It is a serious security issue to ignore the nonce?

@sdelamo
Copy link
Contributor

sdelamo commented Aug 18, 2020

@dgbmariano

Instead of disabling validation (delete NonceClaimValidatorReplacement), just add this class to your project:

package com.example;

import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.cookie.Cookie;
import io.micronaut.security.oauth2.endpoint.nonce.DefaultNonceConfiguration;
import io.micronaut.security.oauth2.endpoint.nonce.persistence.NoncePersistence;
import io.micronaut.security.oauth2.endpoint.nonce.persistence.cookie.CookieNoncePersistenceConfiguration;

import javax.inject.Singleton;
import java.util.Optional;

@Requires(property = DefaultNonceConfiguration.PREFIX + ".persistence", value = "cookie", defaultValue = "cookie")
@Singleton
public class CookieNoncePersistence implements NoncePersistence {

    private final CookieNoncePersistenceConfiguration configuration;

    /**
     * @param configuration The cookie configuration
     */
    public CookieNoncePersistence(CookieNoncePersistenceConfiguration configuration) {
        this.configuration = configuration;
    }

    @Override
    public Optional<String> retrieveNonce(HttpRequest<?> request) {
        Cookie cookie = request.getCookies().get(configuration.getCookieName());
        return Optional.ofNullable(cookie)
                .map(Cookie::getValue);
    }

    @Override
    public void persistNonce(HttpRequest<?> request, MutableHttpResponse response, String nonce) {
        Cookie cookie = Cookie.of(configuration.getCookieName(), nonce);
        cookie.configure(configuration, request.isSecure());
        response.cookie(cookie);
    }
}

This will persist the nonce into a cookie and you will be able to validate it with the regular validator (without disabling it).

This will be shipped into the micronuat security module see(linked PRS) in the next patch version.

@dgbmariano
Copy link
Author

Thanks @sdelamo!

Micronaut Development automation moved this from In progress to Done Aug 19, 2020
@dgbmariano
Copy link
Author

dgbmariano commented Aug 24, 2020

@sdelamo I was creating a test to make this workaround pass the coverage quality gate test, and when I tested retrieveNonce I received the following error:

not yet implemented
java.lang.UnsupportedOperationException: not yet implemented
	at io.micronaut.http.client.netty.NettyClientHttpRequest.getCookies(NettyClientHttpRequest.java:174)
	at com.loggi.security.CookieNoncePersistence.retrieveNonce(CookieNoncePersistence.kt:22)
	at com.loggi.security.CookieNoncePersistenceTest.test retrieve nonce(CookieNoncePersistenceTest.kt:23)

This function is being called? Because according to the test and the file NettyClientHttpRequest.java getCookies is not implemented. Did I messed up in the test?

The test file (incomplete):

import io.micronaut.http.HttpRequest
import io.micronaut.http.cookie.Cookie
import io.micronaut.security.oauth2.endpoint.nonce.persistence.cookie.CookieNoncePersistenceConfiguration
import io.micronaut.test.annotation.MicronautTest
import org.junit.jupiter.api.Test
import javax.inject.Inject

@MicronautTest
class CookieNoncePersistenceTest {
    @Inject
    lateinit var configuration: CookieNoncePersistenceConfiguration

    @Inject
    lateinit var cookieNoncePersistence: CookieNoncePersistence

    @Test
    fun `test retrieve nonce`() {
        val nonce = Cookie.of("OPENID_NONCE", "nonce")
        val request = HttpRequest.GET<String>("/test").cookie(nonce)

        cookieNoncePersistence.retrieveNonce(request)
    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
info: workaround available A workaround is available for the issue relates-to: oidc type: bug Something isn't working
Projects
No open projects
Development

Successfully merging a pull request may close this issue.

2 participants