วิธีทำ Kong ให้รับ JWT + วิธี Generate JWT Token ด้วย Quarkus (Smallrye JWT)
Tags: quarkus, jwt, java, kong
Published: 2020-10-12 14:52:51

---
title: "วิธีทำ Kong ให้รับ JWT + วิธี Generate JWT Token ด้วย Quarkus (Smallrye JWT)"
published: true
description: ""
tags: ["quarkus", "jwt","java", "kong"]
//cover_image: https://direct_url_to_image.jpg
---

#### กว่าจะทำได้กินเวลาไป 2 ชม.เต็มๆ 

เรื่องมันเริ่มจากอยากใช้ Kong ในการ Authentication ผ่าน JWT ที่ Sign แบบ RS256 (RSA) เพื่อให้มีความปลอดภัยขั้นสูงนิดนึง (นิดเดียวจริงๆ) ทางฝั่ง Consumer ก็คือ Sign JWT จาก Private Key ไป ทางฝั่งเราก้อขอแค่ Public Key สำหรับ Decryption แค่นั้นพอ

แต่... แล้วพอทำจริงมันก็ช่างยากเย็นซะเหลือเกิน อาจจะเพราะเราใช้ Quarkus ในการ Generate JWT Token ด้วย ตานี้ RSA Private มันเลยแปลกๆ เลยติดอยู่ตรงนี้ซะนาน 

_เอาล่ะมาเริ่มกันเลยแล้วกัน Step มีดังนี้ !!_

### ขั้น 1 Generate public/private key 

เริ่มที่ Private key ก่อน (ถ้าเค้าถามก็ใส่ Subject ไปให้ครบ ตัวนี้ล่ะสำคัญเลยล่ะ C,O,CN)

```shell
$ openssl req -x509 -newkey rsa:2048 -nodes -keyout ./private-key.pem -days 365
```

ต่อไปก็ Generate เอา Public key

```shell
$ openssl rsa -in private-key.pem -out pub-key.pem -outform PEM -pubout
```

ได้มาสองอันแระเป็น 
* private-key.pem (ซึ่งเราจะเอาไปใส่ใน App เพื่อ Gen)
* pub-key.pem (เราจะเอาตัวนี้ไปเพิ่มใน Consumer Credential ของ Kong)

### ขั้น 2 เอา public key ไปใส่ใน Kong

ก่อนอื่นต้องเท้าความว่าเราใช้แบบ unsecured admin port สำหรับ Kong ก็จะเป็นการยิง curl รัวๆ ตามนี้

* เพิ่ม Consumer
```shell
$ curl -s -X POST http://$HOST:8001/consumers -d 'username=rawin' -d 'custom_id=rawin' | jq
{                                                
  "custom_id": "rawin",                          
  "created_at": 1602513121,                      
  "id": "3c3693c2-db73-4740-8d96-4e46645c4874",  
  "tags": null,                                  
  "username": "rawin"                            
}                                                
```

* เพิ่ม RS256 Public Key 
```shell
$ curl -s -X POST http://$H:32001/consumers/rawin/jwt -F 'rsa_public_key=@pub-key.pem' | jq
{
  "created_at": 1602513212,
  "id": "6467fcdc-ac73-48a6-b2d6-c78a633b59f9",
  "tags": null,
  "secret": "sIjCL1CWZvCye0N9w8op8aHcQGRYxzdf",
  "rsa_public_key": "-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnilPbARTGCm19Tq4xxeT\r\ngeQUfXzAmr7LMpGkcvFiAKf0Yo0hi9SX55B85d0l22EEk5mhwLXcA+orXB8dq9ke\r\nS0mjWjFedXPQfjFCRUyIyQYt75qaDX5RJX+p6YN/VJCZkjlSI1UeaX8rOOmcBuUG\r\nHinV+qMWIZmwvpFXt45HLIuCH2iLM+7/TiiIBHakNR/ogKTKQE7iHon9ZQ2eSakG\r\nUMOBcCrGG8itp2jXtk0oTlZgNtB5+i03HoyZqAZ6Ny7VX0EIlJFuukvuFbNzKZaI\r\nIs21/xnXlsqto7iP9rJViFLzswg1c41nkbpwC1oByvTUDH4pQ94+L1vdhSof6A7n\r\nDwIDAQAB\r\n-----END PUBLIC KEY-----\r\n",
  "consumer": {
    "id": "3c3693c2-db73-4740-8d96-4e46645c4874"
  },
  "key": "9SGoxUxJ8m8V7TaftQxtvBMMEwF3Q2mm",
  "algorithm": "HS256"
}
```

* จากนั้นต้องมีแก้ไข algorithm ด้วยนิดหน่อยโดย 
```shell
$ curl -s -d 'algorithm=RS256' -X PATCH http://$H:32001/consumers/rawin/jwt/6467fcdc-ac73-48a6-b2d6-c78a633b59f9 | jq      
{                                                                                                                          
  "created_at": 1602513212,                                                                                                
  "id": "6467fcdc-ac73-48a6-b2d6-c78a633b59f9",                                                                            
  "tags": null,                                                                                                            
  "secret": "sIjCL1CWZvCye0N9w8op8aHcQGRYxzdf",                                                                            
  "rsa_public_key": "-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnilPbARTGCm19Tq4xxeT\r\ngeQU
fXzAmr7LMpGkcvFiAKf0Yo0hi9SX55B85d0l22EEk5mhwLXcA+orXB8dq9ke\r\nS0mjWjFedXPQfjFCRUyIyQYt75qaDX5RJX+p6YN/VJCZkjlSI1UeaX8rOOm
cBuUG\r\nHinV+qMWIZmwvpFXt45HLIuCH2iLM+7/TiiIBHakNR/ogKTKQE7iHon9ZQ2eSakG\r\nUMOBcCrGG8itp2jXtk0oTlZgNtB5+i03HoyZqAZ6Ny7VX0
EIlJFuukvuFbNzKZaI\r\nIs21/xnXlsqto7iP9rJViFLzswg1c41nkbpwC1oByvTUDH4pQ94+L1vdhSof6A7n\r\nDwIDAQAB\r\n-----END PUBLIC KEY--
---\r\n",                                                                                                                  
  "consumer": {                                                                                                            
    "id": "3c3693c2-db73-4740-8d96-4e46645c4874"                                                                           
  },                                                                                                                       
  "key": "9SGoxUxJ8m8V7TaftQxtvBMMEwF3Q2mm",                                                                               
  "algorithm": "RS256"                                                                                                     
}
```

เป็นอันเรียบร้อยที่ฝั่ง Kong (อันนี้คือเรามีตัว JWT Plugins ลงไว้เป็น Global Mode แล้วนะ)

### ขั้นที่ 3 Generate JWT Token จาก Quarkus 

ขั้นนี้มีซับซ้อนนิดหน่อยคือ เราต้องเอา field "key" มาใส่ในช่อง issuer ของ JWT token ด้วยโดยสมมุติว่าเรามี Quarkus Project อยู่แล้วก็เพิ่ม Extensions ไปก่อนโดย

```shell
$ ./mvnw quarkus:add-extensions -Dextension="quarkus-smallrye-jwt"
```

ต่อมาก็ Copy File *private-key.pem* ไปใส่ที่ *src/main/resources*

แล้วไปเพิ่มในไฟล์  *application.properties* หนึ่งบรรทัดตามด้านล่างนี้

```properties
smallrye.jwt.sign.key-location=private-key.pem
```

ถือว่าอ้างอิง Private key เสร็จสิ้นก็ทำ RestEasy Resource Class มาได้เลย

```java
package org.rawin.jwt;

import javax.enterprise.context.RequestScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import io.smallrye.jwt.build.Jwt;

@Path("/key")
@RequestScoped
public class KeyResource {
    
    @GET
    public String generateKey() throws Exception {
        
        String jwtOut = Jwt.claims()
            .issuer("9SGoxUxJ8m8V7TaftQxtvBMMEwF3Q2mm") // เอาค่าจาก key มา
            .sign();
        return jwtOut;
    }
}
```
จบตามนี้ก็พร้อมรัน

### ขั้นที่ 4 ทดสอบ

หา JWT Token ก่อน
```shell
$ ./mvnw quarkus:dev
```

แล้วเรียก 

```shell
$ curl -s http://localhost:8080/key
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJlS042NTA2UzhlaXp5T2RYZzNUbFpoUUdhZEYxdWJFOSIsImlhdCI6MTYwMjUxMjIwMSwiZXhwIjoxNjAyNTEyNTAxLCJqdGkiOiIzSVpkYjRua0lBelJadW5YQ0dqQWVRIn0.M3KojWn78l9sAbcEqsekiKd-ukJktboRRs6CkrPz6JNfq03pSBTygu_Xf678Z45NGfTJy0wg6ftZj9x1h7GN4VBZ06nqm0Vqcmdx-st_thFswEVOrUlTxzNULMElOejJCybpYd2lwxA_3vYu_L5sj0fyvLfetev90zV0TDK8S91Xl_dfr75fo7hF1jPcdN_wmV3woB3YKhN3xV9zAJpn7F5o1oL2YaRutevVJMqGhidLgb1S8j0H8jB2smr3DZ7v8KNkkMvuCej0oU9f39BixNBsbgm7VnaWC-xoJqBGAEnSIeza00datSyp-NSZN-CB7gLISmunR3S_PQ4iIVGJCg
```

เอาคีย์มาเรียกเข้าที่ Kong

เรียกแบบไม่ใส่ JWT Token
```shell
$ curl -s http://$HOST:8000/office/reports
{"message":"Unauthorized"}
```

เรียกแบบใส่ JWT Token
```shell
$ curl -s  http://$HOST:8000/office/reports -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJlS042NTA2UzhlaXp5T2RYZzNUbFpoUUdhZEYxdWJFOSIsImlhdCI6MTYwMjUxMjIwMSwiZXhwIjoxNjAyNTEyNTAxLCJqdGkiOiIzSVpkYjRua0lBelJadW5YQ0dqQWVRIn0.M3KojWn78l9sAbcEqsekiKd-ukJktboRRs6CkrPz6JNfq03pSBTygu_Xf678Z45NGfTJy0wg6ftZj9x1h7GN4VBZ06nqm0Vqcmdx-st_thFswEVOrUlTxzNULMElOejJCybpYd2lwxA_3vYu_L5sj0fyvLfetev90zV0TDK8S91Xl_dfr75fo7hF1jPcdN_wmV3woB3YKhN3xV9zAJpn7F5o1oL2YaRutevVJMqGhidLgb1S8j0H8jB2smr3DZ7v8KNkkMvuCej0oU9f39BixNBsbgm7VnaWC-xoJqBGAEnSIeza00datSyp-NSZN-CB7gLISmunR3S_PQ4iIVGJCg" 
[]
```

ถือว่าประสบความสำเร็จ รีบมาเขียนไว้นี่คือเก็บไว้ดูเผื่อลืม

ใครสนใจหรือมีคำถามมาคุยกันที่ twitter: [@rawinng](https://twitter.com/rawinng) ครับ