title: How to implement Recaptcha on keycloak login page
body: <p>I want to implement recaptcha in keycloak login page like registration page. I extended UsernamePasswordForm class with desired factory class. I even implenmented action required classes as well. but still i can not see that in provider tab to add in login. i modified existing login.ftl also but no luck.</p>
  <p>below is what i tried.</p>
  <p>My Authenticator class:</p>
  <pre><code>public class MyLoginAuthenticator extends UsernamePasswordForm {
  
      @Override
      public void action(AuthenticationFlowContext context) {
          MultivaluedMap&lt;String, String&gt; formData = context.getHttpRequest().getDecodedFormParameters();
          if (formData.containsKey(&quot;cancel&quot;)) {
              context.cancelLogin();
              return;
          }
  
          if (!validateForm(context, formData)) {
              return;
          }
          context.success();
      }
  
      protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap&lt;String, String&gt; formData) {
          return validateUserAndPassword(context, formData);
      }
  
      @Override
      public void authenticate(AuthenticationFlowContext context) {
          MultivaluedMap&lt;String, String&gt; formData = new MultivaluedMapImpl&lt;&gt;();
          String loginHint = context.getAuthenticationSession().getClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
  
          String rememberMeUsername = AuthenticationManager.getRememberMeUsername(context.getRealm(), context.getHttpRequest().getHttpHeaders());
  
          if (loginHint != null || rememberMeUsername != null) {
              if (loginHint != null) {
                  formData.add(AuthenticationManager.FORM_USERNAME, loginHint);
              } else {
                  formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
                  formData.add(&quot;rememberMe&quot;, &quot;on&quot;);
              }
          }
          Response challengeResponse = challenge(context, formData);
          context.challenge(challengeResponse);
      }
  
      @Override
      public boolean requiresUser() {
          return false;
      }
  
      protected Response challenge(AuthenticationFlowContext context, MultivaluedMap&lt;String, String&gt; formData) {
          LoginFormsProvider forms = context.form();
  
          if (formData.size() &gt; 0) forms.setFormData(formData);
  
          return forms.createLogin();
      }
  
      @Override
      public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
          // never called
          return true;
      }
  
      @Override
      public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
          // never called
      }
  
      @Override
      public void close() {
  
      }
  }
  </code></pre>
  <p>My Factory Class:</p>
  <pre><code>public class LoginAuthenticatorFactory extends UsernamePasswordFormFactory {
  
      public static final String G_RECAPTCHA_RESPONSE = &quot;g-recaptcha-response&quot;;
      public static final String RECAPTCHA_REFERENCE_CATEGORY = &quot;login-recaptcha&quot;;
      public static final String SITE_KEY = &quot;site.key&quot;;
      public static final String SITE_SECRET = &quot;secret&quot;;
      public static final String PROVIDER_ID = &quot;auth-username-password-form-recaptcha&quot;;
  
      public static final MyLoginAuthenticator SINGLETON = new MyLoginAuthenticator();
  
      @Override
      public String getDisplayType() {
          System.out.println(&quot;Ranveer Singh getDisplayType &quot;);
          return &quot;Login Recaptcha&quot;;
      }
  
      @Override
      public String getReferenceCategory() {
          return RECAPTCHA_REFERENCE_CATEGORY;
      }
  
      @Override
      public Authenticator create(KeycloakSession session) {
          return SINGLETON;
      }
  
  
      @Override
      public boolean isConfigurable() {
          return true;
      }
  
      private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.DISABLED};
  
      @Override
      public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
          return REQUIREMENT_CHOICES;
      }
  
      public void buildPage(FormContext context, LoginFormsProvider form) {
          System.out.println(&quot;Ranveer Singh buildPage&quot;);
          AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
          if (captchaConfig == null || captchaConfig.getConfig() == null || captchaConfig.getConfig().get(SITE_KEY) == null || captchaConfig.getConfig().get(SITE_SECRET) == null) {
              form.addError(new FormMessage(null, Messages.RECAPTCHA_NOT_CONFIGURED));
              return;
          }
          String siteKey = captchaConfig.getConfig().get(SITE_KEY);
          form.setAttribute(&quot;recaptchaRequired&quot;, true);
          form.setAttribute(&quot;recaptchaSiteKey&quot;, siteKey);
          form.addScript(&quot;https://www.google.com/recaptcha/api.js&quot;);
      }
  
      public void validate(ValidationContext context) {
          System.out.println(&quot;Ranveer Singh validate&quot;);
          MultivaluedMap&lt;String, String&gt; formData = context.getHttpRequest().getDecodedFormParameters();
          List&lt;FormMessage&gt; errors = new ArrayList&lt;&gt;();
          boolean success = false;
          context.getEvent().detail(Details.REGISTER_METHOD, &quot;form&quot;);
  
          String captcha = formData.getFirst(G_RECAPTCHA_RESPONSE);
          if (!Validation.isBlank(captcha)) {
              AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
              String secret = captchaConfig.getConfig().get(SITE_SECRET);
  
              success = validateRecaptcha(context, success, captcha, secret);
          }
          if (success) {
              context.success();
          } else {
              errors.add(new FormMessage(null, Messages.RECAPTCHA_FAILED));
              formData.remove(G_RECAPTCHA_RESPONSE);
              context.error(Errors.INVALID_REGISTRATION);
              context.validationError(formData, errors);
              return;
  
  
          }
      }
  
      protected boolean validateRecaptcha(ValidationContext context, boolean success, String captcha, String secret) {
          System.out.println(&quot;Ranveer Singh &quot;);
          HttpClient httpClient = context.getSession().getProvider(HttpClientProvider.class).getHttpClient();
          HttpPost post = new HttpPost(&quot;https://www.google.com/recaptcha/api/siteverify&quot;);
          List&lt;NameValuePair&gt; formparams = new LinkedList&lt;&gt;();
          formparams.add(new BasicNameValuePair(&quot;secret&quot;, secret));
          formparams.add(new BasicNameValuePair(&quot;response&quot;, captcha));
          formparams.add(new BasicNameValuePair(&quot;remoteip&quot;, context.getConnection().getRemoteAddr()));
          try {
              UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, &quot;UTF-8&quot;);
              post.setEntity(form);
              HttpResponse response = httpClient.execute(post);
              InputStream content = response.getEntity().getContent();
              try {
                  Map json = JsonSerialization.readValue(content, Map.class);
                  Object val = json.get(&quot;success&quot;);
                  success = Boolean.TRUE.equals(val);
              } finally {
                  content.close();
              }
          } catch (Exception e) {
              ServicesLogger.LOGGER.recaptchaFailed(e);
          }
          return success;
      }
  
      @Override
      public boolean isUserSetupAllowed() {
          return false;
      }
  
  
      @Override
      public void close() {
  
      }
  
      @Override
      public void init(Config.Scope config) {
  
      }
  
      @Override
      public void postInit(KeycloakSessionFactory factory) {
  
      }
  
      @Override
      public String getId() {
          return PROVIDER_ID;
      }
  
      @Override
      public String getHelpText() {
          return &quot;Adds Google Recaptcha button.  Recaptchas verify that the entity that is registering is a human.  This can only be used on the internet and must be configured after you add it.&quot;;
      }
  
      private static final List&lt;ProviderConfigProperty&gt; configProperties = new ArrayList&lt;ProviderConfigProperty&gt;();
  
      static {
          ProviderConfigProperty property;
          property = new ProviderConfigProperty();
          property.setName(SITE_KEY);
          property.setLabel(&quot;Recaptcha Site Key&quot;);
          property.setType(ProviderConfigProperty.STRING_TYPE);
          property.setHelpText(&quot;Google Recaptcha Site Key&quot;);
          configProperties.add(property);
          property = new ProviderConfigProperty();
          property.setName(SITE_SECRET);
          property.setLabel(&quot;Recaptcha Secret&quot;);
          property.setType(ProviderConfigProperty.STRING_TYPE);
          property.setHelpText(&quot;Google Recaptcha Secret&quot;);
          configProperties.add(property);
  
      }
  
      @Override
      public List&lt;ProviderConfigProperty&gt; getConfigProperties() {
          return configProperties;
      }
  
  }
  </code></pre>
  <p>Is there anything that i am missing ? can somebody help me to get recaptcha in login page. Has anybody done this before  ? can someone share sample code so that i can see and try more.</p>
  <p>Thanks in advance.</p>