Captcha API Basics

Liferay provides a headless API to retrieve and submit captchas using the SimpleCAPTCHA engine. Using the /captcha endpoint from the API Explorer, you can add captchas in your custom implementations without using a tag library. There are two endpoints:

  • /GET - Retrieve a Base64 encoded captcha image string and a JWT token for validation
  • /POST - Send the answer of the captcha along with the JWT token for verification

Getting a Captcha Challenge

Start a new Liferay DXP instance by running

docker run -it -m 8g -p 8080:8080 liferay/dxp:2024.q2.11

Sign in to Liferay at http://localhost:8080 using the email address test@liferay.com and the password test. When prompted, change the password to learn.

Once Liferay is running,

  1. Download and unzip Captcha API Basics.

    curl https://resources.learn.liferay.com/dxp/latest/en/installation-and-upgrades/securing-liferay/developer-guide/liferay-p6s7.zip -O
    
    unzip liferay-p6s7.zip
    
  2. Use the cURL script to retrieve a captcha image string and a validation token. On the command line, navigate to the curl folder. Execute the Captcha_GET_FromInstance.sh script.

    ./Captcha_GET_FromInstance.sh
    

    The JSON response shows the captcha image string and the token:

    {
       "image" : "data:image/png;base64,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",
       "token" : "BnDK5SupcZFFKqlBiswtjHv0tw6ptDYoICH8Y/wccQAwmJzS+pvjBxSiPkDaEwnDcuvHCWuHb4slvrdSZVy3W5N2EDNbDRjljs9ksftAkp8s3Fa6bKYiu4hYsCCCCwJA"
    }
    
  3. Copy the value of the image field without the data:image/png;base64, prefix and run the following command:

    echo <COPIED_IMAGE_STRING> | base64 -d > captcha.png
    

    This saves the captcha image in the current directory with the filename captcha.png. Open the file to view the captcha.

    View the saved captcha from the Base64 encoded string.

  4. From the previous JSON response, copy the value of the token field. You’ll use this token to validate the captcha through the /POST endpoint.

  5. Alternatively, call the REST service using the Java client. Navigate into the java folder and compile the source files:

    javac -classpath .:* *.java
    
  6. Run the Captcha_GET_FromInstance class.

    java -classpath .:* Captcha_GET_FromInstance
    

Examine the cURL Command

The Captcha_GET_FromInstance.sh script calls the REST service with a cURL command.

curl \
"http://localhost:8080/o/captcha/v1.0/captcha/challenge" \
--user "test@liferay.com:learn"

Here are the command’s arguments:

ArgumentsDescription
"http://localhost:8080/o/captcha/v1.0/captcha/challenge"Specify the REST service endpoint.
--user "test@liferay.com:learn"Enter basic authentication credentials.
Note

Basic authentication is used here for demonstration purposes. For production, you should authorize users via OAuth2. See Using OAuth2 to Authorize Users for a sample React application using OAuth2.

Examine the Java Class

The Captcha_GET_FromInstance.java class retrieves a captcha string and token by calling the CaptchaResource service.

public static void main(String[] args) throws Exception {
	CaptchaResource.Builder builder = CaptchaResource.builder();

	CaptchaResource captchaResource = builder.authentication(
		"test@liferay.com", "learn"
	).build();

	Captcha captcha = captchaResource.getCaptchaChallenge();

	System.out.println("Token: " + captcha.getToken());

	byte[] imageBytes = Base64.getDecoder(
	).decode(
		captcha.getImage(
		).split(
			","
		)[1]
	);

	try (FileOutputStream fileOutputStream = new FileOutputStream(
			"captcha.png")) {

		fileOutputStream.write(imageBytes);
	}
}

}

This class invokes the REST service using only three lines of code:

Line (abbreviated)Description
CaptchaResource.Builder builder = ...Get a Builder for generating a CaptchaResource service instance.
CaptchaResource captchaResource = builder.authentication(...).build();Use basic authentication and generate a CaptchaResource service instance.
captchaResource.getCaptchaChallenge();Call the captchaResource.getCaptchaChallenge method.

After retrieving the response, the token is displayed, and the Base64 class is used to decode the image string into bytes. This gets saved as an image in the same directory.

Note that the project includes the com.liferay.captcha.rest.client.jar file as a dependency. You can find client JAR dependency information for all REST applications in the API explorer in your installation at /o/api (e.g., http://localhost:8080/o/api).

Note

The main method’s comment demonstrates running the class.

Important

See CaptchaResource for service details.

Post Captcha Response

After retrieving the captcha image, you can use the /POST endpoint to submit your answer and token with a cURL or Java command. Replace abcd with your answer and efgh with your token.

Examine the Captcha_POST_ToInstance cURL Command

Command:

./Captcha_POST_ToInstance.sh abcd efgh

Code:

curl \
"http://localhost:8080/o/captcha/v1.0/captcha/response" \
--data-raw '
	  {
		 "answer": "'${1}'",
		 "token": "'${2}'"
	  }' \
--header "Content-Type: application/json" \
--request "POST" \
--user "test@liferay.com:learn" \

The Captcha_POST_ToInstance.sh script calls the REST service with a cURL command.

Here are the command’s arguments:

ArgumentsDescription
"http://localhost:8080/o/captcha/v1.0/captcha/response"Specify the REST service endpoint.
--data-raw '{ "answer": "'${1}'", "token": "'${2}'"}'Enter the data to post.
--header "Content-Type: application/json"Set the request body format to JSON.
--request "POST"Set the HTTP method to invoke at the specified endpoint.
--user "test@liferay.com:learn"Enter basic authentication credentials.

The API returns returns a 204 if the answer is valid. The JWT token has a nonce tracked by the /POST endpoint. You cannot reuse the same token if you’ve already sent the wrong answer.

Examine the Captcha_POST_ToInstance class

Command:

java -classpath .:*  -Danswer=abcd -Dtoken=efgh Captcha_POST_ToInstance

Code:

public static void main(String[] args) throws Exception {
	CaptchaResource.Builder builder = CaptchaResource.builder();

	CaptchaResource captchaResource = builder.authentication(
		"test@liferay.com", "learn"
	).build();

	captchaResource.postCaptchaResponse(
		new Captcha() {
			{
				answer = String.valueOf(System.getProperty("answer"));
				token = String.valueOf(System.getProperty("token"));
			}
		});
}

The Captcha_POST_ToInstance.java class sends the captcha answer along with a token by calling the CaptchaResource service.

This class invokes the REST service using only three lines of code:

Line (abbreviated)Description
CaptchaResource.Builder builder = ...Get a Builder for generating a CaptchaResource service instance.
CaptchaResource captchaResource = builder.authentication(...).build();Use basic authentication and generate a CaptchaResource service instance.
captchaResource.postCaptchaResponse(...);Call the captchaResource.postCaptchaResponse method.

The postCaptchaResponse() method accepts an instance of the Captcha class with the captcha answer and the JWT token. The API returns a 204 if the answer is valid. The JWT token has a nonce tracked by the /POST endpoint. You cannot reuse the same token if you’ve already sent the wrong answer.

Sending an Incorrect Captcha or Using an Invalid/Expired Token

If you send an incorrect captcha answer with the correct token, you receive the following response:

{
  "status" : "BAD_REQUEST",
  "title" : "Answer is invalid",
  "type" : "CaptchaTextException"
}

If you send the right answer with the same token that was already used, you receive the following response:

{
  "status" : "BAD_REQUEST",
  "title" : "Token: BnDK5SupcZFFKqlBiswtjHv0tw6ptDYoICH8Y/wccQAwmJzS+pvjBxSiPkDaEwnDcuvHCWuHb4slvrdSZVy3W5N2EDNbDRjljs9ksftAkp8s3Fa6bKYiu4hYsCCCCwJA",
  "type" : "IllegalArgumentException"
}

You get the same response for trying to use a token that’s expired.

The API Explorer shows the Captcha services and schemas and has an interface to test each service.

Capabilities

Product

Contact Us

Connect

Powered by Liferay
© 2024 Liferay Inc. All Rights Reserved • Privacy Policy