[JAVA] Try adding SMS authentication (using Twilio) to Keycloak's authentication process

Introduction

Today on the 5th day is Keycloak customization. For a description of Keycloak, see What is Keycloak. This article introduces Keycloak in last year's Keycloak by OpenStandia Advent Calendar 2017. I also participated in last year's Keycloak Advent Calendar and wrote an article about Keycloak Customization Points. This time, we will pick up the authentication function (Authentication SPI) in this customization point and SMS for authentication processing. Let's customize it to add an authentication (identity verification) function.

SMS? SMS authentication?

SMS (Short Message Service) is a service that allows you to send and receive short messages by phone number. SMS authentication is to authenticate (identify) using SMS. Expressed in a picture, it is the following image.

SMS認証イメージ.png

By sending and confirming the authentication code to Mr. A's registered phone number separately from the ID / PWD login, you can confirm your identity more reliably, so you can increase the probability of preventing unauthorized login from a third party. I will.

SMS sending and receiving services are provided by Twilio, nexmo, [Amazon SNS]( There are several such as https://aws.amazon.com/jp/sns/), but this time we used ** Twilio ** </ font> for the SMS sending and receiving service. I will.

What is Twilio?

20171206203748.png

Twilio is a cloud communications company located in San Francisco that provides services for sending and receiving phone calls and text messages using the web service API. In Japan, KDDI is the agency and Japanese document is also open to the public, so I think it will be easy to enter when using it for the first time.

As an aside, Twilio's Advent Calendar was also registered this year. (Registered every year since 2015) Twilio offers various communication APIs other than sending and receiving SMS, so please refer to it if you are interested.

Advance preparation

Create a Twilio account

To use Twilio's SMS authentication, you need a Twilio account (to be exact, you need an API-KEY for sending and receiving SMS) Free Trial Account can be registered and is free up to a certain amount in Twilio It can be used in. When you reach a certain amount, your account will be automatically suspended and you will be notified of upgrades to your paid account.

You will not be charged unless you upgrade to a paid account (as of December 2018), but it may change in the future, so please check the terms of use when registering. Registration itself is easy, so I think you can proceed normally. Phone number verification (SMS) will occur during registration, so prepare a phone that can receive SMS. (SMS communication charges are borne by the recipient.)

Get Twilio API-KEY

After creating an account, when the TOP screen is displayed, go to "Top screen" ⇒ "Phone number verification" ⇒ "Phone number verification Project dashboard" ⇒ "Verify" and the following screen will be displayed. Let's proceed in the order of the numbers.

verify.png

  • 「1. Let’s verify a phone number on your Twilio account」

  • Here, we will verify the phone number again.

  • 「2. Create an application and get your API credentials」

  • Create application and API. Once you create an application, you will be able to check / update the SMS transmission / reception history and API-KEY.

  • 「3. Send your first Verify code」

  • Use the application API-KEY created above to send the SMS verification code with cURL. The contents of the request will be displayed as shown below, so you can also check the API-KEY here. (You can also check from the setting screen)

verify2.png

  • 「4. Get verified」
  • Check cURL to see if the verification code you received is correct.

This completes the preparations on the Twilio side.

Login confirmation before customization

Check the login process before customizing. You can confirm your login by accessing the account management screen.

  • Display account management screen
  • http: // localhost: 8080 / auth / realms / realm name / account

Login.png

  • Successful login to the account management screen

Login_OK.png

I have confirmed that I can log in by just entering the Username / Password.

Authentication function customization

Customize the authentication function. The work order is as follows.

  1. Create screen (Themes)
  2. Create SMS authentication module
  3. Reflect the customization module in Keycloak
  4. Start Keycloak
  5. Reflect settings (Management Console)

Create screen (Themes)

I created a new Themes called "openstandia" to create two screens (.ftl) and a message file (.properties). The source code is here. To reflect it, place the ʻopenstandiafolder of the source code as it is underKEYCLOAK_HOME / themes /` and it will be reflected.

  • Authentication code input screen (sms-validation.ftl)

verifyinput.png

  • Authentication code error screen (sms-validation-error.ftl)

verifyerror.png

  • Message file (* .properties)
  • messages_ja.properties (Japanese messages)
  • messages_en.properties

We have prepared two message files, one for Japanese (jp) and one for English (en). The corresponding language is set in locales in theme.properties ..

_KEYCLOAK_HOME_/themes/Theme name/login/theme.properties


parent=keycloak
import=common/keycloak
locales=en,ja

How to customize Themes was last year's "[Organize Keycloak customization points-About Themes](https://qiita.com/yoonis/items/298d580f7e93e56ddf6a#themes%E3%81%AB%E3%81%A4" % E3% 81% 84% E3% 81% A6) ”, so please refer to it.

SMS authentication module creation

Create an SMS authentication module. Earlier, when "Getting API-KEY of Twilio", we issued an authentication code with cURL and confirmed the authentication code, but we will execute this in the SMS authentication module.

  • The SPI corresponding to the SMS authentication module is ** Authentication SPI **
  • ʻImplements ʻinterface is ʻorg.keycloak.authentication.Authenticator`

First, create the following two basic classes.

  • SMS authentication module class

    • SMSAuthenticator implements Authenticator
  • Factory class of SMS authentication module class

    • SMSAuthenticatorFactory implements AuthenticatorFactory, ConfigurableAuthenticatorFactory

SMSAuthenticatorFactory implementation

  • getId () method
  • Returns the provider ID. (unique)
	public static final String PROVIDER_ID = "sms-authenticator-with-twilio";

	public String getId() {
		return PROVIDER_ID;
	}
  • getDisplayType () method
  • Returns the screen display name.
	public String getDisplayType() {
		return "Twilio SMS Authentication";
	}
  • ʻIsConfigurable ()` method

  • Returns whether it is an Authenticator that can be set on the screen (administrative console). Returns true if there are items to be entered from the screen. This time, API-KEY etc. will be input, so true

  • getConfigProperties () method

  • Screen (Management Console) Input item definition. I want to input API-KEY, authentication code digits, proxy settings, etc. from the management console, so set them here.

	private static final List<ProviderConfigProperty> configProperties;
	static {
        configProperties = ProviderConfigurationBuilder
                .create()
                .property()
                .name(SMSAuthContstants.CONFIG_SMS_API_KEY)
                .label("API-KEY")
                .type(ProviderConfigProperty.STRING_TYPE)
                .helpText("")
                .add()

                ......

                .property()
                .name(SMSAuthContstants.CONFIG_CODE_LENGTH)
                .label("Code Length")
                .type(ProviderConfigProperty.STRING_TYPE)
                .helpText("")
                .defaultValue(4)
                .add()

                .build();
	}

    ......

	public List<ProviderConfigProperty> getConfigProperties() {
		return configProperties;
	}

  • getRequirementChoices () method
  • Returns the Requirement type to support. This time, set only required (REQUIRED) and disabled (DISABLED). Others are ALTERNATIVE and OPTIONAL.
	private static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
			AuthenticationExecutionModel.Requirement.REQUIRED,
			AuthenticationExecutionModel.Requirement.DISABLED
	};

	public Requirement[] getRequirementChoices() {
		return REQUIREMENT_CHOICES;
	}
  • In order to register the SMSAuthenticatorFactory class, place a definition file that describes the factory class name as shown below under META-INF.

META-INF/services/org.keycloak.authentication.AuthenticatorFactory


jp.openstandia.keycloak.authenticator.SMSAuthenticatorFactory

SMS Authenticator implementation

  • ʻAuthenticate (AuthenticationFlowContext context)` method
  • Authentication process, implement SMS authentication code transmission process with this method
public void authenticate(AuthenticationFlowContext context) {

	......
	
	if (sendVerify.sendSMS(phoneNumber)) { //Send SMS

		Response challenge = context.form().createForm("sms-validation.ftl");
		context.challenge(challenge);

	} else {

        //Returns an error screen when SMS fails
		Response challenge = context.form().setError(new FormMessage("sendSMSCodeErrorMessage"))
				.createForm("sms-validation-error.ftl");
		context.challenge(challenge);
	}

	......
}

What should be noted here is how to return the result. Set the screen to return to Response (Challenge) information (createForm) to context. You can also set an error message (setError) if an error occurs. The setting value of setError`` new FormMessage ("sendSMSCodeErrorMessage") is sendSMSCodeErrorMessage of Themes messages / openstandia / login / messages) This is the message Key defined in the file messages_xx_properties.

  • ʻAction (AuthenticationFlowContext context) `method
  • Implemented the process to check the authentication code entered from the screen with this method
public void action(AuthenticationFlowContext context) {

	MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
	String enteredCode = inputData.getFirst("smsCode");

	......
	
	if (sendVerify.verifySMS(phoneNumber, enteredCode)) { //Authentication code confirmation
		logger.info("verify code check : OK");
        //When the authentication code is OK
		context.success();

	} else {
        //Returns an error screen when authentication fails
		Response challenge = context.form()
				.setAttribute("username", context.getAuthenticationSession().getAuthenticatedUser().getUsername())
				.setError(new FormMessage("invalidSMSCodeMessage")).createForm("sms-validation-error.ftl");
		context.challenge(challenge);
		}

	......
}

Again, pay attention to how to return the result. If the verification code check is successful with verifySMS, this verification result will be OK withcontext.success (). In case of an error, set the display screen and error message in Response (Challenge) and set it to context as in the ʻauthenticate` method.

SMS transmission / confirmation processing

The SMS sending / code verification process implemented in the above SMS Authenticator was implemented using Twilio's API using HttpsURLConnection. SMS transmission (Twilio API call) processing is [here](https://github.com/sangyoon-lee/keycloak-sms-authenticator/blob/master/src/main/java/jp/openstandia/keycloak/authenticator/ api / SMSSendVerify.java)

That's all for module creation. Only the important methods have been introduced here. The source code of the sample module is published on GitHub, so please check the source code for detailed settings. The source code is here

Reflect customization module in Keycloak

Reflect the created file in Keycloak.

Placement of Themes

Place the created Themes file (per Openstandia folder) in _KEYCLOAK_HOME_ / themes /. If it has already been placed at the time of "Create screen (Themes)", skip it.

Placement of authentication module (.jar)

Package the authentication module you created with the mvn package and place the jar file in_KEYCLOAK_HOME_ / standalone / deployments /.

Start Keycloak

After deploying all the customization modules, start Keycloak.

Reflect settings (Management Console)

Reflection of Themes

Reflects Themes. This is done from the "Realm Settings"-"Themes" tab. Since only the login theme is created this time, change the "login theme" to "openstandia". Other themes will not be changed.

themes.png

Authentication flow modification

Modify the authentication flow from the management console to add the SMS authentication module to the authentication process.

"Management Console"-"Authentication"-"Browser" authentication flow screen

console1.png

Copy the "Browser" authentication flow (because the default "Browser" authentication flow cannot be modified directly). The authentication flow name is arbitrary.

console2.png

Copyed "Openstandia Browser" authentication flow

console3.png

Click Actions in Openstandia Browser Forms and click Add Execution.

console4.png

Select the module created this time. The display name here is the name set in getDisplayType of SMSAuthenticatorFactory.

console5.png

Added "Twilio SMS Authentication" and moved the execution order above "OTP Form". "OTP Form" is set to disabled (DISABLED) (optional)

console6.png

Click "Actions" in "Twilio SMS Authentication" and then click "Settings". The screen items set in getConfigProperties of SMSAuthenticatorFactory are displayed here.

console7.png

  • "API-KEY" sets the API-KEY obtained from Twilio.
  • If you need a proxy, set Proxy Enabled to On and enter the proxy information.
  • "SMS Code Length" is the number of digits of the verification code to be sent.

Return to the authentication flow and click the "Binding" tab. Change the browser flow to "Openstandia browser" which is a copy.

console8.png

This completes the management console changes.

Operation check

Add the phone number information to the user attributes to log in in advance. Match the "Key" of the phone number attribute with the attribute name in the authentication module.

user_telnum.png

Log in to the account management screen (http: // localhost: 8080 / auth / realms / realm name / account)

Login.png

The verification code input screen is displayed.

verifyinput.png

The verification code will be sent to the entered phone number by SMS.

認証コード.png

Enter the verification code

verify1.png

Login is complete

verifyok.png

  • The following is an error screen when you enter the wrong code. The error message "Authentication code does not match" is the message set by setError of Response (Challenge).

verifyerror.png

This is the end of the operation check.

Finally

I customized the authentication SPI and added SMS authentication to the authentication process. Keycloak customization is not limited to authentication SPI, and the customization method for all SPIs is basically the same.

Simply put

  • Check the SPI that corresponds to the function to be customized. (This time, authentication SPI)

  • Check ʻinterface that the feature you want to customize should be ʻimplements. (This time Authenticator)

  • Implement the method provided by ʻimplements the target ʻinterface.

Only this.

When implementing the third method, we will implement it while understanding what role each method plays. (Because this is the main, it takes the longest time, but ...)

Once you have experienced customizing any SPI, you will be able to implement it smoothly even if you customize another SPI from now on. Please try to customize Keycloak: +1:

Reference material

mark.png

Recommended Posts

Try adding SMS authentication (using Twilio) to Keycloak's authentication process
Try adding text to an image in Scala using the Java standard library
(Android) Try to display static text using DataBinding
Try to make a music player using Basic Player