###### 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なら、実際のハンドラを実行する。