Search code examples
oauth-2.0google-oauthfusionauthgoogle-sso

Callback after Google login throwing error in FusionAuth


We are using the FusionAuth (1.7.4) login screens with our SPA and have Google configured as an IdP in FusionAuth and set up Google OAuth client credentials as discussed here: https://fusionauth.io/docs/v1/tech/identity-providers/google

When the user clicks on the Google login button and authenticates on Google, the browser is sent to https://ident.<mydomain>/oauth2/callback which shows an error in the FusionAuth UI 'missing_redirect_url'.

The full URL for the callback looks like this (shortened with {{variables}} here for clarity):

https://ident.{{SPA_domain}}/oauth2/callback?token={{tokenstring}}&identityProviderId={{fusion_IdP_Id}}&state=client_id%3D{{SPA_App_ID}}%26metaData.device.name%3DMac%2520Safari%26metaData.device.type%3DBROWSER%26nonce%3D%26redirect_uri%3Dhttps%253A%252F%252Fapi.proxy.{{SPA_domain}}%252F{{SPA_name}}%252FOAuthLoginFlowHandler%253Fclient_id%253D {{SPA_App_ID}}%26response_type%3Dcode%26scope%3D%26state%3Dexample%26timezone%3DAustralia%252FSydney

The redirect_uri that FusionAuth claims is missing is double encoded and it exists behind the 'state' parameter that is missing the value and subsequent & delimiter before the client_id.

If we manually correct the state parameter then the callback generates a '500 Internal Server Error.' google is sending the token back to our fusion idp. we are not sure how fusion handles that token and redirects the user to the app. We thought google would send code to fusion and fusion would exchange that code for token. We have debugging enabled for the Google IdP but don't get any useful logging.

If the social login implementation documentation could be improved to explain the complete flow and different implementation techniques that would help a lot.

FusionAuth Logs:

Sep 07, 2019 9:20:05.108 PM ERROR io.fusionauth.app.primeframework.error.ExceptionExceptionHandler - An unhandled exception was thrown
java.lang.NullPointerException: null
    at org.primeframework.mvc.parameter.el.Expression.setCurrentValue(Expression.java:93)
    at org.primeframework.mvc.parameter.el.DefaultExpressionEvaluator.setValue(DefaultExpressionEvaluator.java:129)
    at io.fusionauth.app.action.oauth2.CallbackAction.decodeAndRestoreState(CallbackAction.java:158)
    at io.fusionauth.app.action.oauth2.CallbackAction.get(CallbackAction.java:85)
    at sun.reflect.GeneratedMethodAccessor432.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.primeframework.mvc.util.ReflectionUtils.invoke(ReflectionUtils.java:436)
    at org.primeframework.mvc.action.DefaultActionInvocationWorkflow.execute(DefaultActionInvocationWorkflow.java:84)
    at org.primeframework.mvc.action.DefaultActionInvocationWorkflow.perform(DefaultActionInvocationWorkflow.java:64)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.validation.DefaultValidationWorkflow.perform(DefaultValidationWorkflow.java:47)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.security.DefaultSecurityWorkflow.perform(DefaultSecurityWorkflow.java:60)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.parameter.DefaultPostParameterWorkflow.perform(DefaultPostParameterWorkflow.java:50)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.content.DefaultContentWorkflow.perform(DefaultContentWorkflow.java:52)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.parameter.DefaultParameterWorkflow.perform(DefaultParameterWorkflow.java:57)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.parameter.DefaultURIParameterWorkflow.perform(DefaultURIParameterWorkflow.java:102)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.scope.DefaultScopeRetrievalWorkflow.perform(DefaultScopeRetrievalWorkflow.java:58)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.message.DefaultMessageWorkflow.perform(DefaultMessageWorkflow.java:45)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.action.DefaultActionMappingWorkflow.perform(DefaultActionMappingWorkflow.java:126)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.workflow.StaticResourceWorkflow.perform(StaticResourceWorkflow.java:97)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.parameter.RequestBodyWorkflow.perform(RequestBodyWorkflow.java:89)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.security.DefaultSavedRequestWorkflow.perform(DefaultSavedRequestWorkflow.java:57)
    at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43)
    at org.primeframework.mvc.workflow.DefaultMVCWorkflow.perform(DefaultMVCWorkflow.java:91)
    at org.primeframework.mvc.workflow.DefaultWorkflowChain.continueWorkflow(DefaultWorkflowChain.java:44)
    at org.primeframework.mvc.servlet.FilterWorkflowChain.continueWorkflow(FilterWorkflowChain.java:50)
    at org.primeframework.mvc.servlet.PrimeFilter.doFilter(PrimeFilter.java:84)
    at com.inversoft.maintenance.servlet.MaintenanceModePrimeFilter.doFilter(MaintenanceModePrimeFilter.java:59)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at com.inversoft.servlet.UTF8Filter.doFilter(UTF8Filter.java:27)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

NOTE : We are using authorization_code grant in the regular login flow. So we would like to reuse the same for social logins


Solution

  • There is a bug in the way FusionAuth is decoding the state parameter on the way back from Google.

    The bug is revealed when the redirect_uri contains a URL with request parameters such as https://acme.com/oauth/callback?client_id=b3bef9c1-ba42-414b-aca1-8d30c9252d36.

    This will be fixed in the upcoming 1.8.1 or 1.9.0 version release. In the meantime, a workaround would be to use a URL segment if that is possible for you or use the state parameter instead to ensure you have the necessary information on the redirect URL.

    Thanks for reporting!