Paseto


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

속성
타입
설명
기본값

Next

func(*Ctx) bool

미들웨어를 스킵할 함수 정의

nil

SuccessHandler

func(*fiber.Ctx) error

유효한 토큰에 대해 실행될 함수 정의

c.Next()

ErrorHandler

func(*fiber.Ctx, error) error

유효하지 않은 토큰에 대해 실행될 함수 정의

401 Invalid or expired PASETO

Validate

PayloadValidator

페이로드가 유효한지 검증할 함수 정의. 선택 사항. CreateToken 함수로 생성된 페이로드를 사용하는 경우. 다른 함수로 토큰이 생성되면, 이 함수를 반드시 제공해야 함

nil

SymmetricKey

[]byte

토큰 암호화에 사용될 비밀키. 존재하면 미들웨어가 로컬 토큰을 생성

nil

PrivateKey

ed25519.PrivateKey

토큰 서명에 사용될 비밀키. 존재하면(PublicKey와 함께) 미들웨어가 공개 토큰을 생성

nil

PublicKey

crypto.PublicKey

토큰 검증에 사용될 공개키. 존재하면(PrivateKey와 함께) 미들웨어가 공개 토큰을 생성

nil

ContextKey

string

토큰의 사용자 정보를 컨텍스트에 저장할 때 사용할 컨텍스트 키

"auth-token"

TokenLookup

[2]string

요청에서 토큰을 추출하는 데 사용되는 크기가 2인 문자열 슬라이스

["header","Authorization"]

Usage

이 미들웨어를 사용할 때, 인증을 위한 토큰 생성 시 pasetoware.CreateToken 함수를 사용할 수 있습니다. 이 함수는 토큰을 생성하고, 암호화하거나 서명한 후 PASETO 토큰을 리턴합니다.

Config에 SymmetricKey를 전달하면 로컬(암호화된) 토큰이 생성되고, PublicKeyPrivateKey를 전달하면 공개(서명된) 토큰이 생성됩니다.

자체 데이터 구조를 사용하려면 paseware.ConfigValidate 함수를 제공해야 합니다. 이 함수는 토큰에 저장된 데이터와 오류를 리턴합니다.

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"  

응답

Welcome john

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"

응답

Welcome John Doe  

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"

응답

Welcome john  

Last updated