iris/middleware/jwt/jwt_test.go
2020-10-17 15:22:42 +03:00

335 lines
8.9 KiB
Go

// Package jwt_test contains simple Iris jwt tests. Most of the jwt functionality is already tested inside the jose package itself.
package jwt_test
import (
"os"
"testing"
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/httptest"
"github.com/kataras/iris/v12/middleware/jwt"
)
type userClaims struct {
// Optionally:
Issuer string `json:"iss"`
Subject string `json:"sub"`
Audience jwt.Audience `json:"aud"`
//
Username string `json:"username"`
}
const testMaxAge = 7 * time.Second
// Random RSA verification and encryption.
func TestRSA(t *testing.T) {
j := jwt.RSA(testMaxAge)
t.Cleanup(func() {
os.Remove(jwt.DefaultSignFilename)
os.Remove(jwt.DefaultEncFilename)
})
testWriteVerifyBlockToken(t, j)
}
// HMAC verification and encryption.
func TestHMAC(t *testing.T) {
j := jwt.HMAC(testMaxAge, "secret", "itsa16bytesecret")
testWriteVerifyBlockToken(t, j)
}
func TestNew_HMAC(t *testing.T) {
j, err := jwt.New(testMaxAge, jwt.HS256, []byte("secret"))
if err != nil {
t.Fatal(err)
}
err = j.WithEncryption(jwt.A128GCM, jwt.DIRECT, []byte("itsa16bytesecret"))
if err != nil {
t.Fatal(err)
}
testWriteVerifyBlockToken(t, j)
}
// HMAC verification only (unecrypted).
func TestVerify(t *testing.T) {
j, err := jwt.New(testMaxAge, jwt.HS256, []byte("another secret"))
if err != nil {
t.Fatal(err)
}
testWriteVerifyBlockToken(t, j)
}
func testWriteVerifyBlockToken(t *testing.T, j *jwt.JWT) {
t.Helper()
j.UseBlocklist()
j.Extractors = append(j.Extractors, jwt.FromJSON("access_token"))
customClaims := &userClaims{
Issuer: "an-issuer",
Audience: jwt.Audience{"an-audience"},
Subject: "user",
Username: "kataras",
}
app := iris.New()
app.OnErrorCode(iris.StatusUnauthorized, func(ctx iris.Context) {
if err := ctx.GetErr(); err != nil {
// Test accessing the private error and set this as the response body.
ctx.WriteString(err.Error())
} else { // Else the default behavior
ctx.WriteString(iris.StatusText(iris.StatusUnauthorized))
}
})
app.Get("/auth", func(ctx iris.Context) {
j.WriteToken(ctx, customClaims)
})
app.Post("/protected", func(ctx iris.Context) {
var claims userClaims
_, err := j.VerifyToken(ctx, &claims)
if err != nil {
// t.Logf("%s: %v", ctx.Path(), err)
ctx.StopWithError(iris.StatusUnauthorized, iris.PrivateError(err))
return
}
ctx.JSON(claims)
})
m := app.Party("/middleware")
m.Use(j.Verify(func() interface{} {
return new(userClaims)
}))
m.Post("/protected", func(ctx iris.Context) {
claims := jwt.Get(ctx)
ctx.JSON(claims)
})
m.Post("/invalidate", func(ctx iris.Context) {
ctx.Logout() // OR j.Invalidate(ctx)
})
e := httptest.New(t, app)
// Get token.
rawToken := e.GET("/auth").Expect().Status(httptest.StatusOK).Body().Raw()
if rawToken == "" {
t.Fatalf("empty token")
}
restrictedPaths := [...]string{"/protected", "/middleware/protected"}
now := time.Now()
for _, path := range restrictedPaths {
// Authorization Header.
e.POST(path).WithHeader("Authorization", "Bearer "+rawToken).Expect().
Status(httptest.StatusOK).JSON().Equal(customClaims)
// URL Query.
e.POST(path).WithQuery("token", rawToken).Expect().
Status(httptest.StatusOK).JSON().Equal(customClaims)
// JSON Body.
e.POST(path).WithJSON(iris.Map{"access_token": rawToken}).Expect().
Status(httptest.StatusOK).JSON().Equal(customClaims)
// Missing "Bearer".
e.POST(path).WithHeader("Authorization", rawToken).Expect().
Status(httptest.StatusUnauthorized).Body().Equal("token is missing")
}
// Invalidate the token.
e.POST("/middleware/invalidate").WithQuery("token", rawToken).Expect().
Status(httptest.StatusOK)
// Token is blocked by server.
e.POST("/middleware/protected").WithQuery("token", rawToken).Expect().
Status(httptest.StatusUnauthorized).Body().Equal("token is blocked")
expireRemDur := testMaxAge - time.Since(now)
// Expiration.
time.Sleep(expireRemDur /* -end */)
for _, path := range restrictedPaths {
e.POST(path).WithQuery("token", rawToken).Expect().
Status(httptest.StatusUnauthorized).Body().Equal("token is expired (exp)")
}
}
func TestVerifyMap(t *testing.T) {
j := jwt.HMAC(testMaxAge, "secret", "itsa16bytesecret")
expectedClaims := iris.Map{
"iss": "tester",
"username": "makis",
"roles": []string{"admin"},
}
app := iris.New()
app.Get("/user/auth", func(ctx iris.Context) {
err := j.WriteToken(ctx, expectedClaims)
if err != nil {
ctx.StopWithError(iris.StatusUnauthorized, err)
return
}
if expectedClaims["exp"] == nil || expectedClaims["iat"] == nil {
ctx.StopWithText(iris.StatusBadRequest,
"exp or/and iat is nil - this means that the expiry was not set")
return
}
})
userAPI := app.Party("/user")
userAPI.Post("/", func(ctx iris.Context) {
var claims iris.Map
if _, err := j.VerifyToken(ctx, &claims); err != nil {
ctx.StopWithError(iris.StatusUnauthorized, iris.PrivateError(err))
return
}
ctx.JSON(claims)
})
// Test map + Verify middleware.
userAPI.Post("/middleware", j.Verify(nil), func(ctx iris.Context) {
claims := jwt.Get(ctx)
ctx.JSON(claims)
})
e := httptest.New(t, app, httptest.LogLevel("error"))
token := e.GET("/user/auth").Expect().Status(httptest.StatusOK).Body().Raw()
if token == "" {
t.Fatalf("empty token")
}
e.POST("/user").WithHeader("Authorization", "Bearer "+token).Expect().
Status(httptest.StatusOK).JSON().Equal(expectedClaims)
e.POST("/user/middleware").WithHeader("Authorization", "Bearer "+token).Expect().
Status(httptest.StatusOK).JSON().Equal(expectedClaims)
e.POST("/user").Expect().Status(httptest.StatusUnauthorized)
}
type customClaims struct {
Username string `json:"username"`
Token string `json:"token"`
}
func (c *customClaims) SetToken(tok string) {
c.Token = tok
}
func TestVerifyStruct(t *testing.T) {
maxAge := testMaxAge / 2
j := jwt.HMAC(maxAge, "secret", "itsa16bytesecret")
app := iris.New()
app.Get("/user/auth", func(ctx iris.Context) {
err := j.WriteToken(ctx, customClaims{
Username: "makis",
})
if err != nil {
ctx.StopWithError(iris.StatusUnauthorized, err)
return
}
})
userAPI := app.Party("/user")
userAPI.Post("/", func(ctx iris.Context) {
var claims customClaims
if _, err := j.VerifyToken(ctx, &claims); err != nil {
ctx.StopWithError(iris.StatusUnauthorized, iris.PrivateError(err))
return
}
ctx.JSON(claims)
})
e := httptest.New(t, app)
token := e.GET("/user/auth").Expect().Status(httptest.StatusOK).Body().Raw()
if token == "" {
t.Fatalf("empty token")
}
e.POST("/user").WithHeader("Authorization", "Bearer "+token).Expect().
Status(httptest.StatusOK).JSON().Object().ContainsMap(iris.Map{
"username": "makis",
"token": token, // Test SetToken.
})
e.POST("/user").Expect().Status(httptest.StatusUnauthorized)
time.Sleep(maxAge)
e.POST("/user").WithHeader("Authorization", "Bearer "+token).Expect().Status(httptest.StatusUnauthorized)
}
func TestVerifyJSON(t *testing.T) {
j := jwt.HMAC(testMaxAge, "secret", "itsa16bytesecret")
app := iris.New()
app.Get("/user/auth", func(ctx iris.Context) {
err := j.WriteToken(ctx, iris.Map{"roles": []string{"admin"}})
if err != nil {
ctx.StopWithError(iris.StatusUnauthorized, err)
return
}
})
app.Post("/", j.VerifyJSON(), func(ctx iris.Context) {
claims := struct {
Roles []string `json:"roles"`
}{}
jwt.ReadJSON(ctx, &claims)
ctx.JSON(claims)
})
e := httptest.New(t, app, httptest.LogLevel("error"))
token := e.GET("/user/auth").Expect().Status(httptest.StatusOK).Body().Raw()
if token == "" {
t.Fatalf("empty token")
}
e.POST("/").WithHeader("Authorization", "Bearer "+token).Expect().
Status(httptest.StatusOK).JSON().Equal(iris.Map{"roles": []string{"admin"}})
e.POST("/").Expect().Status(httptest.StatusUnauthorized)
}
func TestVerifyUserAndExpected(t *testing.T) { // Tests the jwt.User struct + context validator + expected.
maxAge := testMaxAge / 2
j := jwt.HMAC(maxAge, "secret", "itsa16bytesecret")
expectedUser := j.NewUser(jwt.Username("makis"), jwt.Roles("admin"), jwt.Fields(iris.Map{
"custom": true,
})) // only for the sake of the test, we iniitalize it here.
expectedUser.Issuer = "tester"
app := iris.New()
app.Get("/user/auth", func(ctx iris.Context) {
tok, err := expectedUser.GetToken()
if err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
ctx.WriteString(tok)
})
userAPI := app.Party("/user")
userAPI.Use(jwt.WithExpected(jwt.Expected{Issuer: "tester"}, j.VerifyUser()))
userAPI.Post("/", func(ctx iris.Context) {
user := ctx.User()
ctx.JSON(user)
})
e := httptest.New(t, app)
token := e.GET("/user/auth").Expect().Status(httptest.StatusOK).Body().Raw()
if token == "" {
t.Fatalf("empty token")
}
e.POST("/user").WithHeader("Authorization", "Bearer "+token).Expect().
Status(httptest.StatusOK).JSON().Equal(expectedUser)
// Test generic client message if we don't manage the private error by ourselves.
e.POST("/user").Expect().Status(httptest.StatusUnauthorized).Body().Equal("Unauthorized")
}