keycloak: Login with Github errors with class cast exception when user chooses to keep their email addresses private
Describe the bug
Keycloak errors out with an IDENTITY_PROVIDER_LOGIN_ERROR and shows a 502 error page when a user chooses to login with Github. This only occurs when the user has decided to make their email address private on Github pages. Irrespective of what the user chooses to show or not show on their public Github account, an OIDC exchange for credentials should return back a email address if the scope has the appropriate entries (user:email, in this case).
Version
15.0.2
Expected behavior
Irrespective of whether the user opts to show their email on their public profile on Github, when doing an OIDC flow, the service provider should be able to access the user’s email address if they include the appropriate scope parameter.
Actual behavior
How to Reproduce?
-
Connect an application that uses keycloak to provide the ability to ‘Login with Github’.

-
Login with a Github account that has the box “Keep my email addresses private” checked.

-
Keycloak will return as error page.

Anything else?
Need to investigate how the user profile is accessed by keycloak during this exchange.
Context: On GitHub profile settings, under Public email, some users (including myself) have that option set to “Don’t show my email address.” The call to /user only returns the public email address, so if the user doesn’t want it shown, the API will respect that. To get the user’s email addresses regardless of whether or not they are public, use the call to /user/emails with the appropriate access token included.
I believe the error is from here:
Caused by: org.keycloak.broker.provider.IdentityBrokerException: Could not obtain user email from github.
at org.keycloak.keycloak-services@15.0.2//org.keycloak.social.github.GitHubIdentityProvider.searchEmail(GitHubIdentityProvider.java:108)
at org.keycloak.keycloak-services@15.0.2//org.keycloak.social.github.GitHubIdentityProvider.doGetFederatedIdentity(GitHubIdentityProvider.java:87)
... 78 more
Caused by: java.lang.ClassCastException: class com.fasterxml.jackson.databind.node.ObjectNode cannot be cast to class com.fasterxml.jackson.databind.node.ArrayNode (com.fasterxml.jackson.databind.node.ObjectNode and com.fasterxml.jackson.databind.node.ArrayNode are in unnamed module of loader 'com.fasterxml.jackson.core.jackson-databind@2.12.1' @27b76147)
at org.keycloak.keycloak-services@15.0.2//org.keycloak.social.github.GitHubIdentityProvider.searchEmail(GitHubIdentityProvider.java:98)
... 79 more
Full Keycloak logs with error lines:
11:15:03,177 ERROR [org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider] (default task-18) Failed to make identity provider oauth callback: org.keycloak.broker.provider.IdentityBrokerException: Could not obtain user profile from github.
at org.keycloak.keycloak-services@15.0.2//org.keycloak.social.github.GitHubIdentityProvider.doGetFederatedIdentity(GitHubIdentityProvider.java:92)
at org.keycloak.keycloak-services@15.0.2//org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider.getFederatedIdentity(AbstractOAuth2IdentityProvider.java:306)
at org.keycloak.keycloak-services@15.0.2//org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider$Endpoint.authResponse(AbstractOAuth2IdentityProvider.java:502)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:138)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:546)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:435)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:396)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:358)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:398)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:365)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:150)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:110)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:141)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:104)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:440)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:358)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:245)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:61)
at org.jboss.resteasy.resteasy-jaxrs@3.15.1.Final//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at javax.servlet.api@2.0.0.Final//javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at org.keycloak.keycloak-wildfly-extensions@15.0.2//org.keycloak.provider.wildfly.WildFlyRequestFilter.lambda$doFilter$0(WildFlyRequestFilter.java:41)
at org.keycloak.keycloak-services@15.0.2//org.keycloak.services.filters.AbstractRequestFilter.filter(AbstractRequestFilter.java:43)
at org.keycloak.keycloak-wildfly-extensions@15.0.2//org.keycloak.provider.wildfly.WildFlyRequestFilter.doFilter(WildFlyRequestFilter.java:39)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.core@2.2.5.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.core@2.2.5.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.core@2.2.5.Final//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.core@2.2.5.Final//io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.core@2.2.5.Final//io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at io.undertow.core@2.2.5.Final//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.core@2.2.5.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.core@2.2.5.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
at io.undertow.core@2.2.5.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:78)
at io.undertow.servlet@2.2.5.Final//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:99)
at io.undertow.core@2.2.5.Final//io.undertow.server.Connectors.executeRootHandler(Connectors.java:387)
at io.undertow.core@2.2.5.Final//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:841)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at org.jboss.xnio@3.8.4.Final//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1280)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: org.keycloak.broker.provider.IdentityBrokerException: Could not obtain user email from github.
at org.keycloak.keycloak-services@15.0.2//org.keycloak.social.github.GitHubIdentityProvider.searchEmail(GitHubIdentityProvider.java:108)
at org.keycloak.keycloak-services@15.0.2//org.keycloak.social.github.GitHubIdentityProvider.doGetFederatedIdentity(GitHubIdentityProvider.java:87)
... 78 more
Caused by: java.lang.ClassCastException: class com.fasterxml.jackson.databind.node.ObjectNode cannot be cast to class com.fasterxml.jackson.databind.node.ArrayNode (com.fasterxml.jackson.databind.node.ObjectNode and com.fasterxml.jackson.databind.node.ArrayNode are in unnamed module of loader 'com.fasterxml.jackson.core.jackson-databind@2.12.1' @27b76147)
at org.keycloak.keycloak-services@15.0.2//org.keycloak.social.github.GitHubIdentityProvider.searchEmail(GitHubIdentityProvider.java:98)
... 79 more
11:15:03,181 WARN [org.keycloak.events] (default task-18) type=IDENTITY_PROVIDER_LOGIN_ERROR, realmId=demo, clientId=xxx, userId=null, ipAddress=172.17.0.1, error=identity_provider_login_failure, code_id=cb103222-cc1e-4283-94c4-515f9c586a5b, authSessionParentId=cb103222-cc1e-4283-94c4-515f9c586a5b, authSessionTabId=ev8gVnMQEtU
11:17:05,613 WARN [org.keycloak.events] (default task-18) type=LOGIN_ERROR, realmId=demo, clientId=xxx, userId=null, ipAddress=172.17.0.1, error=user_not_found, auth_method=openid-connect, auth_type=code, redirect_uri=http://localhost:8081/blah/, code_id=cb103222-cc1e-4283-94c4-515f9c586a5b, authSessionParentId=cb103222-cc1e-4283-94c4-515f9c586a5b, authSessionTabId=iY_2P6iSQxE
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 18 (5 by maintainers)
Commits related to this issue
- Improve error management in the github provider Closes https://github.com/keycloak/keycloak/issues/9429 — committed to rmartinc/keycloak by rmartinc a year ago
- Improve error management in the github provider Closes https://github.com/keycloak/keycloak/issues/9429 — committed to rmartinc/keycloak by rmartinc a year ago
- Improve error management in the github provider Closes https://github.com/keycloak/keycloak/issues/9429 — committed to rmartinc/keycloak by rmartinc a year ago
- Improve error management in the github provider Closes https://github.com/keycloak/keycloak/issues/9429 — committed to keycloak/keycloak by rmartinc a year ago
@rmartinc, problem found.
It was hard to build your branch, so I decided to go with minimal nodejs app to see raw request responses.
There are two types of applications can be created with GitHub,
GitHub AppandOAuth App.In both cases, profile returns with
emailfield set tonullifPublic emailis not set.If using
OAuth App, response to/user/emailsreturns as you point out, all user emails are available to Keycloak at once.If one goes with
GitHub App, by default/user/emailsreturn:Then from error, it is clear what is the problem. As per GitHub docs, they say both app types are same, with difference of more flexibility on
GitHub App. And really, there is detailedPermissions & Eventssection.Obviously, after enabling
Email Addressespermission forGitHub App, request to/user/emailssuccessfully returns emails as withOAuth App.Why we created
GitHub Appin the first place, I don’t know. I suppose that GitHub prefers it, and default navigation leads toGitHub App.On the other hand, this case should be handled within Keycloak, casting result to
ArrayNode… not checking/reporting status code… meh… 😃Btw, tons of tutorials/blog posts refer to GitHub token as
Iv1...which isGitHub Apptoken, whileOAuth Apptoken is more uniform.This can be mentioned somewhere in documentation as well I suppose.