FusionAuth / fusionauth-issues

FusionAuth issue submission project
https://fusionauth.io
90 stars 12 forks source link

ERROR 500 - Unhandled Exeption at DefaultSAMLv2Service.parseRequest when trying a SP initiated login #186

Closed emidiotorre closed 4 years ago

emidiotorre commented 5 years ago

I am getting a 500 Error page when logging in from an auth0 service provider

Just to give a little bit of a context, i need my users to be able to move from app A (owned by me) to B (an external service) without any hassle, B is using SAML on auth0.com so we configured it. Now when from A i click on a link to B it prompts me a login page, and then redirects me to my fusionauth installation, which after a while gives me a 500 error page.

I have no clue how to fix this.

This is what fusionauth is logging:

Jun 18, 2019 12:14:36.812 PM ERROR io.fusionauth.app.primeframework.error.ExceptionExceptionHandler - An unhandled exception was thrown java.lang.NullPointerException: null at io.fusionauth.samlv2.service.DefaultSAMLv2Service.parseRequest(DefaultSAMLv2Service.java:428) ~[fusionauth-samlv2-0.2.0.jar:0.2.0] at io.fusionauth.app.action.samlv2.LoginAction.get(LoginAction.java:92) ~[fusionauth-app-1.6.1.jar:1.6.1] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_171] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_171] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_171] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_171] at org.primeframework.mvc.util.ReflectionUtils.invoke(ReflectionUtils.java:436) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.action.DefaultActionInvocationWorkflow.execute(DefaultActionInvocationWorkflow.java:84) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.action.DefaultActionInvocationWorkflow.perform(DefaultActionInvocationWorkflow.java:64) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.validation.DefaultValidationWorkflow.perform(DefaultValidationWorkflow.java:47) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.security.DefaultSecurityWorkflow.perform(DefaultSecurityWorkflow.java:60) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.parameter.DefaultPostParameterWorkflow.perform(DefaultPostParameterWorkflow.java:50) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.content.DefaultContentWorkflow.perform(DefaultContentWorkflow.java:52) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.parameter.DefaultParameterWorkflow.perform(DefaultParameterWorkflow.java:57) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.parameter.DefaultURIParameterWorkflow.perform(DefaultURIParameterWorkflow.java:102) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.scope.DefaultScopeRetrievalWorkflow.perform(DefaultScopeRetrievalWorkflow.java:58) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.message.DefaultMessageWorkflow.perform(DefaultMessageWorkflow.java:45) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.action.DefaultActionMappingWorkflow.perform(DefaultActionMappingWorkflow.java:126) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.StaticResourceWorkflow.perform(StaticResourceWorkflow.java:97) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.parameter.RequestBodyWorkflow.perform(RequestBodyWorkflow.java:89) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.security.DefaultSavedRequestWorkflow.perform(DefaultSavedRequestWorkflow.java:57) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.SubWorkflowChain.continueWorkflow(SubWorkflowChain.java:43) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.DefaultMVCWorkflow.perform(DefaultMVCWorkflow.java:91) ~[prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.workflow.DefaultWorkflowChain.continueWorkflow(DefaultWorkflowChain.java:44) [prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.servlet.FilterWorkflowChain.continueWorkflow(FilterWorkflowChain.java:50) [prime-mvc-1.13.3.jar:1.13.3] at org.primeframework.mvc.servlet.PrimeFilter.doFilter(PrimeFilter.java:84) [prime-mvc-1.13.3.jar:1.13.3] at com.inversoft.maintenance.servlet.MaintenanceModePrimeFilter.doFilter(MaintenanceModePrimeFilter.java:59) [inversoft-maintenance-mode-0.12.8.jar:0.12.8] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.31] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.31] at com.inversoft.servlet.UTF8Filter.doFilter(UTF8Filter.java:27) [inversoft-servlet-0.1.1.jar:0.1.1] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.31] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.31] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [catalina.jar:8.5.31] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [catalina.jar:8.5.31] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) [catalina.jar:8.5.31] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [catalina.jar:8.5.31] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [catalina.jar:8.5.31] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [catalina.jar:8.5.31] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [catalina.jar:8.5.31] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-coyote.jar:8.5.31] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-coyote.jar:8.5.31] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-coyote.jar:8.5.31] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468) [tomcat-coyote.jar:8.5.31] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:8.5.31] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_171] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_171] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:8.5.31] at java.lang.Thread.run(Thread.java:748) [na:1.8.0_171]

It would be great to just have a hint on where to move from here. Thanks.

robotdan commented 5 years ago

Hi @emidiotorre I can help you with this.

I don't know yet if this is a bug on our end or not, but the exception is being caused because FusionAuth is expecting a NameIDPolicy in the SAML request and it was not provided.

Taking a brief look at the Auth0 SAML docs... I see a few things regarding the name Id.

nameIdentifierFormat (string): Default is urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified.

https://auth0.com/docs/protocols/saml/saml-configuration/saml-assertions

That default value is correct, it is the default value for the the <NameIDPolicy>. http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf

Do you know if it is possible to get this policy in the SAML request from Auth0?

emidiotorre commented 5 years ago

Thank you for the prompt response @robotdan !

I am trying to fix this with the help of the service provider, I don't think i have any settings available to bypass this check, right?

Probably it is not a bug, but it would be nice to handle the exeption with a clear error message, i saw other people opening issues for similar problems.

Again, thanks.

robotdan commented 5 years ago

No way to bypass it on our end. Let us know what you find out, it is possible we need to be tolerant of this value missing in the request.

robotdan commented 5 years ago

@emidiotorre were you able to get this working with changes to the SAML SP? Let us know if we can be of assistance.

emidiotorre commented 5 years ago

No, it still doesnt work. they say that i need to define this "claims" correctly: "email" = "email" "given_name" = "given_name" "family_name" = "family_name"

as far as i know this values are already mapped correctly, the information about the user are already encoded this way when i check them on my application.

Do I have to define this mappings somewhere? Or could it be the fact that i'm accessing the IDP with a hostname, while the SP is redirecting the user on the ip directly?? Change any settings like the issuer, the audience maybe?? Could I do anything useful with a Lambda function, btw what are reconciliation lambdas used for?

Thank you for the help

robotdan commented 5 years ago

Thanks for the update @emidiotorre .

To clarify, are you configuring FusionAuth as a SAML Identity Provider, or the SAML v2 Service Provider? In other words, do you have FusionAuth users logging into a 3rd party SAML v2 IdP such as Auth0, or is your your third party IdP trying to log into FusionAuth using SAML v2?

My initial assumption was you're configuring FusionAuth as the service provider, but perhaps I'm mistaken.

So the initial exception we saw was because the SAML response did not contain the Name Id Policy - or at least that is what I believe is occurring based upon the exception we observed.

they say that i need to define this "claims" correctly: "email" = "email" "given_name" = "given_name" "family_name" = "family_name"

So, if we are speaking about FusionAuth being the SAML v2 service provider (configuring SAML v2 through the Identity Providers Settings --> Identity Providers) - will look for some standard claims from the SAML IdP.

We attempt to find the given_name and family_name as follows:

After we try both of those, it is possible these values are still null in our User data model, we next call a lambda if defined.

The reconciliation lambda is used when FusionAuth is the SAML v2 service provider and it allows FusionAuth to map any claim returned by the SAML v2 IdP to the FusionAuth user. The lambda will be provided the user, and SAML response object. The lambda can then optionally be used to extract additional values from the SAML response. For example if the first name is provided as given_name in the SAML response you can use a lambda to grab that value.

A rough example would be :

function reconcile(user, registration, samlResponse) {
  // set firstName using the 'given_name' claim in the SAML response
  user.firstName = samlResponse.assertion.attributes.given_name; 
}

If we're talking about the SAML v2 IdP configuration - which is found in the Application configuration, then we'd be talking about the Populate Lambda which is used ensure the SAML response we send back to a service provided initiated login request contains the expected claims.

We are currently sending a response to the SAML v2 service provider that contains the following claims (not exhaustive, but these are the relevant claims based upon the ones you mentioned)

FA property    Claim
--------------------------------------------
email       email
firstName   http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
            first_name
fullName    http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
            name
            full_name
lastName    http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
            last_name

So if this is the problem area, then perhaps all we need to do is create a populate lambda and add the given_name and family_name claims to the response.

An example SAML v2 Populate lambda would be something like:

function populate(samlResponse, user, registration) {
  samlResponse.assertion.attributes['given_name'] = user.firstName;
  samlResponse.assertion.attributes['family_name'] = user.lastName;
}

This example lambda would add given_name and family_name claims to the SAML response.

Let me know which side of SAML you're trying to get working and we can go from there.

emidiotorre commented 5 years ago

I'm using Fusionauth as the IdP, so I tried using a lambda like the last one you provided me, but I'm still getting the same error..

robotdan commented 5 years ago

Have you enabled debug on the SAML config? If so, there may be some event logs available.

System --> Event Log

Are there any errors in the log?

emidiotorre commented 5 years ago

yes, debug is enabled, but no logs

robotdan commented 5 years ago

Ok. SAML can be a pita and is very complex. This all makes it difficult to debug.

If you'd like to join the FusionAuth Slack channel and DM me to share some config perhaps I can recreate the issue to see if it is something we can do differently or if it is a configuration issue with the service provider.

emidiotorre commented 5 years ago

Yes it would be nice, but I think I do need an invitation right?

robotdan commented 5 years ago

Nope, just need to request access. Find the "chat with users" link on https://fusionauth.io/

robotdan commented 5 years ago

@emidiotorre any update on this, anything we can help with?

robotdan commented 5 years ago

Working with @emidiotorre offline, captured the SAML Authn request.

<samlp:AuthnRequest 
       xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" 
       Destination="http://fusionauth:9011/samlv2/login/117c46d7-42a3-57df-3410-50c69d555f2b"           
       ID="_f6d582e90210d1354e45b4c374002e2e"
       IssueInstant="2019-07-10T23:25:07Z" 
       ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
      Version="2.0">
  <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
     urn:[REMOVED]
  </saml:Issuer>
</samlp:AuthnRequest>

This is missing the NameIdPolicy

Here is an example NameIdPolicy attribute.

<samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate="true"/>

Need to identify if this is a required attribute in the Authn request, and the SP should be sending it, or if FusionAuth needs to be able to handle a request such as this one.

robotdan commented 5 years ago

@emidiotorre were you able to get this issue resolved?

emidiotorre commented 5 years ago

Nope, we sadly moved back to the old auth system, hope to get it working in the near future!

robotdan commented 5 years ago

Bummer, thanks for the update. Closing this out for now.

robotdan commented 4 years ago

Another user ran into this as well and pointed out that this is optional according to the SAML spec.

\<NameIDPolicy> [Optional] Specifies constraints on the name identifier to be used to represent the requested subject. If omitted, then any type of identifier supported by the identity provider for the requested subject can be used, constrained by any relevant deployment-specific policies, with respect to privacy, for example.

http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf

We'll review, perhaps we can just default to email if this is omitted from the request.

prasanna10021991 commented 4 years ago

Facing the same issue here due to missing NameIDPolicy as part of the AuthnRequest from Service Provider (Medallia). Here is the sample request from the service provider and the logs causing the 500 error :

https://medallia.com/sso Logs file : [error-logs.txt](https://github.com/FusionAuth/fusionauth-issues/files/3735272/error-logs.txt) found something on this in the SAML2.0 spec mentioning if NameIDPolicy is unavailable in the request the default settings of the IDP should be taken as the identifier. (http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf)
robotdan commented 4 years ago

I think this has been resolved, if anyone is still seeing this issue please re-open.