![](https://github.com/gofiber/contrib/workflows/Linter/badge.svg)
PASETO는 Web Token (PASETO) 인증 미들웨어를 리턴합니다.
유효한 토큰의 경우, 페이로드 데이터를 Ctx.Locals에 설정하고 다음 핸들러를 호출합니다.
유효하지 않은 토큰의 경우, "401 - Unauthorized" 오류를 리턴합니다.
토큰이 누락된 경우, "400 - BadRequest" 오류를 리턴합니다.
주의: Go 1.18 이상 필요
Installation
이 미들웨어는 Fiber v2를 지원합니다.
go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/paseto
go get -u github.com/o1egl/paseto
Signatures
pasetoware.New(config ...pasetoware.Config) func(*fiber.Ctx) error
Config
func(*fiber.Ctx, error) error
401 Invalid or expired PASETO
페이로드가 유효한지 검증할 함수 정의. 선택 사항. CreateToken
함수로 생성된 페이로드를 사용하는 경우. 다른 함수로 토큰이 생성되면, 이 함수를 반드시 제공해야 함
토큰 암호화에 사용될 비밀키. 존재하면 미들웨어가 로컬 토큰을 생성
토큰 서명에 사용될 비밀키. 존재하면(PublicKey와 함께) 미들웨어가 공개 토큰을 생성
토큰 검증에 사용될 공개키. 존재하면(PrivateKey와 함께) 미들웨어가 공개 토큰을 생성
토큰의 사용자 정보를 컨텍스트에 저장할 때 사용할 컨텍스트 키
요청에서 토큰을 추출하는 데 사용되는 크기가 2인 문자열 슬라이스
["header","Authorization"]
Usage
이 미들웨어를 사용할 때, 인증을 위한 토큰 생성 시 pasetoware.CreateToken 함수를 사용할 수 있습니다. 이 함수는 토큰을 생성하고, 암호화하거나 서명한 후 PASETO 토큰을 리턴합니다.
Config에 SymmetricKey
를 전달하면 로컬(암호화된) 토큰이 생성되고, PublicKey
와 PrivateKey
를 전달하면 공개(서명된) 토큰이 생성됩니다.
자체 데이터 구조를 사용하려면 paseware.Config
의 Validate
함수를 제공해야 합니다. 이 함수는 토큰에 저장된 데이터와 오류를 리턴합니다.
Examples
아래는 이 미들웨어 사용을 시작하는 데 도움이 될 몇 가지 예제 목록입니다. 여기에 나와 있지 않은 추가 예제가 필요한 경우 테스트 파일을 참조하세요.
SymmetricKey
package main
import (
"time"
"github.com/gofiber/fiber/v2"
"github.com/o1egl/paseto"
pasetoware "github.com/gofiber/contrib/paseto"
)
const secretSymmetricKey = "symmetric-secret-key (size = 32)"
func main() {
app := fiber.New()
// Login route
app.Post("/login", login)
// Unauthenticated route
app.Get("/", accessible)
// Paseto Middleware with local (encrypted) token
apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{
SymmetricKey: []byte(secretSymmetricKey),
TokenPrefix: "Bearer",
}))
// Restricted Routes
apiGroup.Get("/restricted", restricted)
err := app.Listen(":8088")
if err != nil {
return
}
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Throws Unauthorized error
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Create token and encrypt it
encryptedToken, err := pasetoware.CreateToken([]byte(secretSymmetricKey), user, 12*time.Hour, pasetoware.PurposeLocal)
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": encryptedToken})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("Accessible")
}
func restricted(c *fiber.Ctx) error {
payload := c.Locals(pasetoware.DefaultContextKey).(string)
return c.SendString("Welcome " + payload)
}
Test
사용자 이름과 비밀번호로 로그인해 토큰 받기.
curl --data "user=john&pass=doe" http://localhost:8088/login
응답
{
"token": "v2.local.eY7o9YAJ7Uqyo0JdyfHXKVARj3HgBhqIHckPgNIJOU6u489CXYL6bpOXbEtTB_nNM7nTFpcRVi7YAtJToxbxkkraHmE39pqjnBgkca-URgE-jhZGuhGu7ablmK-8tVoe5iY8mQqWFuJHAznTASUHh4AG55AMUcIALi6pEG28lAgVfw2azvnvbg4JOVZnjutcOVswd-ErsAuGtuEZkTmX7BfaLaO9ZvEX9cHahYPajuRjwU2TQrcpqITg-eYMNA1NuO8OVdnGf0mkUk6ElJUTZqhx4CSSylNXr7IlOwzTbUotEDAQTcNP7IRZI3VfpnRgnmtnZ5s.bnVsbAY"
}
Authorization 요청 헤더에 토큰을 포함해 제한된 리소스 요청
curl localhost:8088/api/restricted -H "Authorization: Bearer v2.local.eY7o9YAJ7Uqyo0JdyfHXKVARj3HgBhqIHckPgNIJOU6u489CXYL6bpOXbEtTB_nNM7nTFpcRVi7YAtJToxbxkkraHmE39pqjnBgkca-URgE-jhZGuhGu7ablmK-8tVoe5iY8mQqWFuJHAznTASUHh4AG55AMUcIALi6pEG28lAgVfw2azvnvbg4JOVZnjutcOVswd-ErsAuGtuEZkTmX7BfaLaO9ZvEX9cHahYPajuRjwU2TQrcpqITg-eYMNA1NuO8OVdnGf0mkUk6ElJUTZqhx4CSSylNXr7IlOwzTbUotEDAQTcNP7IRZI3VfpnRgnmtnZ5s.bnVsbA"
응답
SymmetricKey + 사용자 정의 Validator 콜백
package main
import (
"encoding/json"
"time"
"github.com/o1egl/paseto"
pasetoware "github.com/gofiber/contrib/paseto"
)
const secretSymmetricKey = "symmetric-secret-key (size = 32)"
type customPayloadStruct struct {
Name string `json:"name"`
ExpiresAt time.Time `json:"expiresAt"`
}
func main() {
app := fiber.New()
// Login route
app.Post("/login", login)
// Unauthenticated route
app.Get("/", accessible)
// Paseto Middleware with local (encrypted) token
apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{
SymmetricKey: []byte(secretSymmetricKey),
TokenPrefix: "Bearer",
Validate: func(decrypted []byte) (any, error) {
var payload customPayloadStruct
err := json.Unmarshal(decrypted, &payload)
return payload, err
},
}))
// Restricted Routes
apiGroup.Get("/restricted", restricted)
err := app.Listen(":8088")
if err != nil {
return
}
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Throws Unauthorized error
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Create the payload
payload := customPayloadStruct{
Name: "John Doe",
ExpiresAt: time.Now().Add(12 * time.Hour),
}
// Create token and encrypt it
encryptedToken, err := paseto.NewV2().Encrypt([]byte(secretSymmetricKey), payload, nil)
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": encryptedToken})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("Accessible")
}
func restricted(c *fiber.Ctx) error {
payload := c.Locals(pasetoware.DefaultContextKey).(customPayloadStruct)
return c.SendString("Welcome " + payload.Name)
}
Test
사용자 이름과 비밀번호로 로그인해 토큰 받기.
curl --data "user=john&pass=doe" http://localhost:8088/login
응답
{
"token": "v2.local.OSnDEMUndq8JpRdCD8yX-mr-Z0-Mi85Jw0ftxseiNLCbRc44Mxl5dnn-SV9Qew1n9Y44wXZwm_FG279cILJk7lYc_B_IoMCRBudJE7qMgctkD9UBM-ZRZgCX9ekJh3S1Oo6Erp7bO-omPra5.bnVsbA"
}
Authorization 요청 헤더에 토큰을 포함해 제한된 리소스 요청
curl localhost:8088/api/restricted -H "Authorization: Bearer v2.local.OSnDEMUndq8JpRdCD8yX-mr-Z0-Mi85Jw0ftxseiNLCbRc44Mxl5dnn-SV9Qew1n9Y44wXZwm_FG279cILJk7lYc_B_IoMCRBudJE7qMgctkD9UBM-ZRZgCX9ekJh3S1Oo6Erp7bO-omPra5.bnVsbA"
응답
PublicPrivate Key
package main
import (
"crypto/ed25519"
"encoding/hex"
"time"
"github.com/gofiber/fiber/v2"
pasetoware "github.com/gofiber/contrib/paseto"
)
const privateKeySeed = "e9c67fe2433aa4110caf029eba70df2c822cad226b6300ead3dcae443ac3810f"
var seed, _ = hex.DecodeString(privateKeySeed)
var privateKey = ed25519.NewKeyFromSeed(seed)
type customPayloadStruct struct {
Name string `json:"name"`
ExpiresAt time.Time `json:"expiresAt"`
}
func main() {
app := fiber.New()
// Login route
app.Post("/login", login)
// Unauthenticated route
app.Get("/", accessible)
// Paseto Middleware with local (encrypted) token
apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{
TokenPrefix: "Bearer",
PrivateKey: privateKey,
PublicKey: privateKey.Public(),
}))
// Restricted Routes
apiGroup.Get("/restricted", restricted)
err := app.Listen(":8088")
if err != nil {
return
}
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Throws Unauthorized error
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Create token and encrypt it
encryptedToken, err := pasetoware.CreateToken(privateKey, user, 12*time.Hour, pasetoware.PurposePublic)
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": encryptedToken})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("Accessible")
}
func restricted(c *fiber.Ctx) error {
payload := c.Locals(pasetoware.DefaultContextKey).(string)
return c.SendString("Welcome " + payload)
}
Test
사용자 이름과 비밀번호로 로그인해 토큰 받기.
curl --data "user=john&pass=doe" http://localhost:8088/login
응답
{
"token": "v2.public.eyJhdWQiOiJnb2ZpYmVyLmdvcGhlcnMiLCJkYXRhIjoiam9obiIsImV4cCI6IjIwMjMtMDctMTNUMDg6NDk6MzctMDM6MDAiLCJpYXQiOiIyMDIzLTA3LTEyVDIwOjQ5OjM3LTAzOjAwIiwianRpIjoiMjIzYjM0MjQtNWNkZS00NDFhLWJiZWEtZjBjYWFhYTdiYWFlIiwibmJmIjoiMjAyMy0wNy0xMlQyMDo0OTozNy0wMzowMCIsInN1YiI6InVzZXItdG9rZW4ifWiqK_yg0eJbIs2hnup4NuBYg7v4lxh33zEhEljsH7QUaZXAdtbCPK7cN-NSfSxrw68owwgo-dOlPrD7lc5M_AU.bnVsbA"
}
Authorization 요청 헤더에 토큰을 포함해 제한된 리소스 요청
curl localhost:8088/api/restricted -H "Authorization: Bearer v2.public.eyJhdWQiOiJnb2ZpYmVyLmdvcGhlcnMiLCJkYXRhIjoiam9obiIsImV4cCI6IjIwMjMtMDctMTNUMDg6NDk6MzctMDM6MDAiLCJpYXQiOiIyMDIzLTA3LTEyVDIwOjQ5OjM3LTAzOjAwIiwianRpIjoiMjIzYjM0MjQtNWNkZS00NDFhLWJiZWEtZjBjYWFhYTdiYWFlIiwibmJmIjoiMjAyMy0wNy0xMlQyMDo0OTozNy0wMzowMCIsInN1YiI6InVzZXItdG9rZW4ifWiqK_yg0eJbIs2hnup4NuBYg7v4lxh33zEhEljsH7QUaZXAdtbCPK7cN-NSfSxrw68owwgo-dOlPrD7lc5M_AU.bnVsbA"
응답