Belajar One Time Password (OTP) di Android (bagian 1)

Membuat server OTP dengan Spring Boot dan Heroku

Yudi Setiawan
Nusanet Developers
8 min readJan 13, 2019

--

Series

Pengenalan

One Time Password atau sering disingkat dengan OTP merupakan salah satu metode keamanan pada aplikasi dan umumnya dipakai sebagai keamanan tambahan pada pengaturan Login dimana, kita sering melihatnya pada aplikasi-aplikasi umum sebagai Two Factor Authentication. Berikut gambaran beberapa aplikasi yang menggunakan metode ini.

Contoh penggunaan OTP

Series

Adapun pada artikel ini kita akan bagi menjadi 2 bagian yaitu sebagai berikut.

  • Bagian pertama, kita akan membuat server yang berfungsi untuk mengirimkan kode OTP.
  • Bagian kedua, kita akan membuat aplikasi Android yang berfungsi untuk membaca kode OTP-nya lewat SMS.

Persiapan

Adapun beberapa hal yang kita pakai pada artikel bagian pertama ini ialah sebagai berikut.

  • Spring Boot, untuk membuat server-nya kita menggunakan framework Spring Boot. Alasan mengapa kita pakai Spring Boot ialah karena saya sendiri bisa Java dan Kotlin jadi, saya rasa sudah sewajarnya jika saya memilih Spring Boot. Awalnya sih mau pakai NodeJS namun, karena saya tidak ada basic di JS jadi, akan lebih sulit untuk mempelajarinya.
  • Heroku, untuk menjalankan server yang sudah kita buat tadi maka, kita perlu menjalankannya pada sebuah cloud platform. Dan pilihan saya jatuh kepada Heroku karena gratis dan juga mudah melakukan pengaturannya.
  • PostgreSQL, untuk database-nya saya contohkan pakai PostgreSQL saja. Alasannya mengapa saya pilih PostgreSQL ialah karena di Heroku yang gratis pakai PostgreSQL jadi, saya tidak ada pilihan lain ya 😉.
  • Text Local, untuk SMS gateway-nya kita pakai yang sudah siap pakai dan gratis juga tentunya namun, gratis-nya terbatas ya. Untuk SMS gateway-nya saya pakai https://www.textlocal.com/integrations/api/.

Registrasi SMS Gateway

Untuk mengirim SMS-nya kita memerlukan SMS gateway yang siap pakai saja ataupun jika kita bisa buat sendiri SMS gateway-nya juga tidak ada masalah sih. Untuk SMS gateway-nya silakan kita registrasi di situsnya langsung https://www.textlocal.com/integrations/api/. Untuk proses registrasinya tidak akan saya jelaskan ya. Lalu, jika sudah selesai registrasi coba ambil nilai api key-nya.

Memulai Pembuatan Projek Server

Pertama, silakan buat projek Spring Boot. Pada artikel ini saya pakai IntelliJ IDEA untuk membuat projeknya. Berikut langkah-langkahnya.

Buat projek Spring Boot
Isi keterangan projek
Pilih dependency Web
Pilih dependency JPA dan PostgreSQL
Pilih direktori penyimpanan projek

Setelah selesai buat projeknya coba buka file pom.xml dan pastikan isinya kurang lebih seperti berikut.

Catatan: saya ada menambahkan dependency OkHttp

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ysn</groupId>
<artifactId>server_otp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>server_otp</name>
<description>Demo project for Spring Boot as Server OTP</description>

<properties>
<java.version>1.8</java.version>
<kotlin.version>1.2.71</kotlin.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.5</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180130</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

</project>

Untuk mengetes apakah projek kita sudah benar atau belum coba kita buat endpoint sederhana. Buka file class utamanya atau yang ada method main-nya. Kemudian isi dengan kode berikut.

@RestController
@SpringBootApplication
class ServerOtpApplication {

@GetMapping("/api/index")
fun index(): ResponseEntity<Map<String, Any>> {
val responseData = HashMap<String, Any>()
responseData["time_server"] = System.currentTimeMillis()
return ResponseEntity(responseData, HttpStatus.OK)
}

}

fun main(args: Array<String>) {
runApplication<ServerOtpApplication>(*args)
}

Lalu coba jalankan kode tersebut dari IDE atau dari terminal tinggal ketik perintah berikut.

mvn spring-boot:run

Catatan: pastikan ketika kita menjalankan perintah mvn spring-boot:run harus sudah berada didalam direktori projeknya

Jika sudah running, coba kita buka browser dan ketik localhost:8080/api/index maka, akan keluar respon dari kode yang sudah kita buat tadi.

Hasil response dari endpoint

Jika respon-nya sudah seperti pada gambar diatas maka, projek kita sudah benar pembuatannya.

Pembuatan Database

Untuk database pada artikel ini kita cuma perlu buat satu tabel saja dimana, tabel tersebut dari field-field berikut.

  1. id
  2. code
  3. is_active

Sekarang coba kita buat database-nya terlebih dahulu di PostgreSQL dengan cara buka terminal lalu ketik perintah berikut untuk masuk ke terminal PostgreSQL-nya.

psql

Setelah itu kita buat database-nya dengan cara ketik perintah berikut.

create database server_otp;

Selanjutnya coba kita lihat apakah database-nya sudah berhasil dibuat atau belum dengan perintah berikut.

\l
Proses pembuatan database di PostgreSQL

Selanjutnya coba kita buat table-nya lewat Java Persistence Layer (JPA). Buat file class baru bernama Otp dan isi dengan kode berikut.

@Entity
@Table(name = "Otp")
class Otp (
@GenericGenerator(
name = "otpSequenceGenerator",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = [
Parameter(name = "sequence_name", value = "otpSequence"),
Parameter(name = "initial_value", value = "1"),
Parameter(name = "increment_value", value = "1")
]
)

@GeneratedValue(generator = "otpSequenceGenerator")
@Id
var id: Long = 0L,
var code: String = "",
var isActive: Boolean = false
)

Lalu, kita config pengaturan koneksi database-nya dengan cara buka file application.properties dan isi dengan kode berikut.

# Database properties
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/server_otp
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

Sekarang coba kita jalankan lagi projek Spring Boot-nya dan kemudian cek apakah sudah terbuat table otp di database-nya.

Table otp

Repository dan Service

Repository ini berfungsi sebagai lapisan untuk melakukan semua operasi database secara langsung. Sekarang coba kita buat interface OtpRepository dan isi dengan kode berikut.

@Repository
interface OtpRepository : CrudRepository<Otp, Long> {

fun findByCode(code: String): Otp?

}

Service ini berfungsi sebagai lapisan yang menghubungkan lapisan database ke lapisan controller. Berikut gambaran relasinya.

http://www.skywayperspectives.org/documentation/me4s86/images/AppLayers-SpringMVC.jpg

Sekarang kita buat interface service OtpService seperti berikut.

interface OtpService {

fun createOtp(): String

fun updateOtp(code: String): Boolean

fun deleteOtp(code: String): Boolean

}

Lalu, kita buat class implement-nya dari interface OtpService seperti berikut.

@Service(value = "otpService")
class OtpServiceImpl : OtpService {

@Autowired
private lateinit var otpRepository: OtpRepository

override fun createOtp(): String {
var newCodeOtp: String?
while (true) {
newCodeOtp = ""
for (a in 1..4) {
newCodeOtp += Random().nextInt(10)
}
otpRepository.findByCode(newCodeOtp) ?: break

}
val newOtp = Otp(code = newCodeOtp!!, isActive = true)
otpRepository.save(newOtp)
return newCodeOtp
}

override fun updateOtp(code: String): Boolean {
val otpLocal = otpRepository.findByCode(code)
return if (otpLocal != null && otpLocal.isActive) {
otpLocal.isActive = false
otpRepository.save(otpLocal)
true
} else {
false
}
}

override fun deleteOtp(code: String): Boolean {
val otpLocal = otpRepository.findByCode(code)
otpLocal?.isActive = true
return if (otpLocal != null) {
otpRepository.delete(otpLocal)
true
} else {
false
}
}

}

Controller

Buat file class baru bernama OtpController dan isi dengan kode berikut.

@RestController
@RequestMapping("/api/otp")
class OtpController {

@Autowired
private lateinit var otpService: OtpService

@PostMapping("/send")
fun sendOtp(@RequestParam phoneNumber: String): ResponseEntity<Map<String, Any>> {
val codeOtp = otpService.createOtp()
val formRequestBody = FormBody.Builder()
.add("apikey", "f8NjAKbSJNQ-y5aF90RSBBexj4DoxkbKLYPDoHqzho")
.add("message", codeOtp)
.add("sender", "System")
.add("numbers", phoneNumber)
.build()
val request = Request.Builder()
.url("https://api.txtlocal.com/send/")
.post(formRequestBody)
.build()
val call = OkHttpClient().newCall(request)
val response = call.execute()
val responseData = HashMap<String, Any>()
responseData["time_server"] = System.currentTimeMillis()

val isSuccessful = response.isSuccessful
val responseCode = response.code()
val jsonObjectResponseFromServer = JSONObject(response.body().string())
val responseStatusMessage = jsonObjectResponseFromServer.getString("status")
System.out.println("responseDataFromServer: $jsonObjectResponseFromServer")

if (isSuccessful && responseCode == 200 && responseStatusMessage == "success") {
responseData["success"] = true
} else {
responseData["success"] = false
otpService.deleteOtp(codeOtp)
}
return ResponseEntity(responseData, HttpStatus.OK)
}

@PostMapping("/update")
fun updateOtp(@RequestParam codeOtp: String): ResponseEntity<Map<String, Any>> {
val responseData = HashMap<String, Any>()
responseData["time_server"] = System.currentTimeMillis()
responseData["success"] = otpService.updateOtp(codeOtp)
return ResponseEntity(responseData, HttpStatus.OK)
}

}

Bisa kita lihat pada kode diatas kita ada membuat dua endpoint dengan url seperti berikut.

  • localhost:8080/api/otp/send?phoneNumber=:value berfungsi sebagai pengirim kode OTP-nya dari server kita ke server SMS gateway-nya dengan cara awalnya kita buat terlebih dahulu kode OTP-nya di server kita lalu kita panggil SMS gateway-nya dari server kita dan jika kita dapat respon berhasil dari server SMS gateway-nya maka, kita kembalikan respon berhasil ke client kita namun, jika respon dari SMS gateway-nya gagal maka, kita hapus terlebih dahulu kode OTP-nya yang sebelumnya sudah tersimpan di database kita lalu, kita kirimkan respon gagal ke client.
  • localhost:8080/api/otp/update?codeOtp=:value berfungsi untuk mengubah status kode OTP-nya dari yang aktif (true) menjadi tidak aktif (false) agar kode tersebut tidak bisa dipakai lagi.

Testing Lokal

Sekarang coba kita jalankan lagi projeknya dan testing lokal pakai Postman.

  • Coba panggil endpoint pertama dengan method POST dengan url localhost:8080/api/otp/send?phoneNumber=nomor_hp dimana, nomor_hp-nya itu kita pakai kode negara Indonesia yaitu tambahkan +62 didepannya.
  • Jika sudah berhasil maka, langkah selanjutnya adalah coba kita update status kode OTP tersebut menjadi tidak aktif.
Testing OTP di lokal

Upload projek ke Heroku

Setelah berhasil kita testing di lokal maka, saatnya kita coba upload projek server kita ini ke Heroku. Pada artikel ini tidak akan saya jelaskan bagaimana cara registrasi di Heroku dan hanya saya jelaskan dari awal pembuatan projek di Heroku sampai deploy projek server kita di Heroku. Berikut langkah-langkahnya

Buat projek baru di Heroku
  • Lalu, isi keterangan projek seperti berikut.
Isi keterangan projek
  • Selanjutnya, pilih Create app.
  • Kemudian, langkah berikutnya ikuti keterangan seperti pada gambar berikut.
Setup projek heroku ke projek di lokal

Sebenarnya langkah pada gambar terakhir maksudnya adalah kita harus mengatur git di projek kita agar bisa di-push ke repository kita di Heroku. Jika susah mengikuti langkah-langkah pada gambar terakhir maka, silakan ikut perintah-perintah terminal berikut.

Jika ada gagal dalam proses mengatur heroku di projek lokal maka, pastikan terlebih dahulu bahwa kita telah login ke heroku di terminal-nya dengan perintah heroku login

Jika sudah kita push projek lokal kita ke Heroku maka, tampilannya di terminal kurang lebih seperti berikut dimana, ada keterangan bahwa server kita sudah di-deploy.

Projek kita berhasil di-deploy di Heroku

Testing Heroku

Kemudian, coba kita test lagi dengan cara yang sama di testing lokal namun, kita ganti base url-nya dari localhost:8080 menjadi https://server-otp.herokuapp.com

Testing OTP di Heroku

Kesimpulan

Di artikel bagian pertama ini kita sudah bisa membuat Server OTP-nya. Jadi, di artikel bagian keduanya kita akan bahas bagaimana cara membaca kode OTP-nya lewat SMS di Android. Untuk projek server pada artikel ini bisa dilihat di Github.

--

--