###### tags: `go` `Auth0`
# Golang JWT Auth
jwt ミドルウェアを使って、jwt認証をする。
```bash=
mkdir client
cd client
go mod init go-jwt-tutorial/client
```
# クライント側
```go=
package main
import (
"fmt"
"time"
jwt "github.com/dgrijalva/jwt-go"
)
var mySigningkey = []byte("mysupersecretphrase")
func GenerateJWT() (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["authorized"] = true
claims["user"] = "Elliot Forbes"
claims["exp"] = time.Now().Add(time.Minute * 30).Unix()
tokenString, err := token.SignedString(mySigningkey)
if err != nil {
fmt.Errorf("something weng wrong: %s", err.Error())
return "", err
}
return tokenString, nil
}
func main() {
tokenString, err := GenerateJWT()
if err != nil {
fmt.Println("Error generating token string")
}
fmt.Println(tokenString)
}
```
jwtパッケージを使って、JWT文字列を生成
HMACSHA256 方式
payload
- authorized
- user
- exp : 期限
SigningKey(署名キー) の文字列が同じである限り、
同じペイロードは同じjwtになる。
署名キー(秘密鍵or共通鍵)は環境変数から読み込んで使う方式がおすすめ
```go=
var myStringKey = os.Get("MY_JWT_TOKEN")
```
# サーバー側
secretPageハンドラのレスポンスは、正しくJWT認証できなかったら見れないことにする。
```go=
package main
import "github.com/gin-gonic/gin"
func secretPage(c *gin.Context) {
c.String(200, "super secret Information")
}
func main() {
router := gin.Default()
router.GET("/", secretPage)
router.Run(":8000")
}
```
上記のAPIサーバーに、認証を追加する。
## 認証を設計する方針
ミドルウェア的発送
**isAuthorized()** を使って、クロージャーを返す。
要するに、pythonのデコレーターのように、
gin.HandleFuncを引数にしてHandleFuncを返す関数を使って、
ただのハンドラに認証の追加機能をつける
```go=
func isAuthorized(endpoint gin.HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Header["Token"] != nil {
} else {
c.String(200, "Not Authorized")
}
}
}
func main() {
router := gin.Default()
router.GET("/", isAuthorized(secretPage))
router.Run(":8000")
}
```
## 完成形(HMAC暗号)
```go=
var mySigningKey = []byte("mysupersecretphrase")
func secretPage(c *gin.Context) {
c.String(200, "secret message")
}
func isAuthorized(endpoint gin.HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Header["Token"] != nil {
token, err := jwt.Parse(c.Request.Header["Token"][0], func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("there was an error")
}
return mySigningKey, nil
})
if err != nil {
c.String(200, err.Error())
}
if token.Valid {
endpoint(c)
}
} else {
c.String(200, "Not Authorized")
}
}
}
func main() {
router := gin.Default()
router.GET("/", isAuthorized(secretPage))
router.Run(":8000")
}
```
HMAC(共有鍵方式)なので、同じSigningkey(署名キー)で暗号化、復号化する。
1. リクエストヘッダーに "Token" ヘッダーがあるかどうか
- 実際は Authorization とか
2. もしあれば、中身のトークンを jwt.Parse()メソッドでパース
3. パースされたJWT(暗号化されたまま)をコールバックに受け取るので、復号化する?
4. token.Validというフィールドがtrueなら、実際のハンドラを実行する。