From ed38047385784c4a1fb67a1aede3b8f4796b46a8 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Mon, 2 Nov 2020 18:46:38 +0200 Subject: [PATCH] add an example for sessions + view data as requested --- _examples/README.md | 1 + _examples/auth/jwt/blocklist/main.go | 9 -- _examples/auth/jwt/tutorial/api.go | 25 ++++++ _examples/auth/jwt/tutorial/go.mod | 10 +++ _examples/auth/jwt/tutorial/main.go | 89 ++++++++++++++++++++ _examples/auth/jwt/tutorial/user.go | 63 ++++++++++++++ _examples/sessions/viewdata/main.go | 42 +++++++++ _examples/sessions/viewdata/views/index.html | 11 +++ middleware/jwt/blocklist/redis/blocklist.go | 3 - 9 files changed, 241 insertions(+), 12 deletions(-) create mode 100644 _examples/auth/jwt/tutorial/api.go create mode 100644 _examples/auth/jwt/tutorial/go.mod create mode 100644 _examples/auth/jwt/tutorial/main.go create mode 100644 _examples/auth/jwt/tutorial/user.go create mode 100644 _examples/sessions/viewdata/main.go create mode 100644 _examples/sessions/viewdata/views/index.html diff --git a/_examples/README.md b/_examples/README.md index da8c458b..03706f6d 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -221,6 +221,7 @@ * [Badger](sessions/database/badger/main.go) * [BoltDB](sessions/database/boltdb/main.go) * [Redis](sessions/database/redis/main.go) + * [View Data](sessions/viewdata) * Websocket * [Gorilla FileWatch (3rd-party)](websocket/gorilla-filewatch/main.go) * [Basic](websocket/basic) diff --git a/_examples/auth/jwt/blocklist/main.go b/_examples/auth/jwt/blocklist/main.go index 6549f658..8f3362c8 100644 --- a/_examples/auth/jwt/blocklist/main.go +++ b/_examples/auth/jwt/blocklist/main.go @@ -1,7 +1,6 @@ package main import ( - "context" "time" "github.com/kataras/iris/v12" @@ -62,14 +61,6 @@ func main() { app.Listen(":8080") } -// generateID optionally to set the value for `jwt.ID` on Sign, -// which sets the standard claims value "jti". -// If you use a blocklist with the default Blocklist.GetKey you have to set it. -var generateID = func(*context.Context) string { - id, _ := uuid.NewRandom() - return id.String() -} - func authenticate(ctx iris.Context) { claims := userClaims{ Username: "kataras", diff --git a/_examples/auth/jwt/tutorial/api.go b/_examples/auth/jwt/tutorial/api.go new file mode 100644 index 00000000..688551e9 --- /dev/null +++ b/_examples/auth/jwt/tutorial/api.go @@ -0,0 +1,25 @@ +package main + +import "github.com/kataras/iris/v12" + +func loginView(ctx iris.Context) { + +} + +func login(ctx iris.Context) { + +} + +func logout(ctx iris.Context) { + ctx.Logout() + + ctx.Redirect("/", iris.StatusTemporaryRedirect) +} + +func createTodo(ctx iris.Context) { + +} + +func getTodo(ctx iris.Context) { + +} diff --git a/_examples/auth/jwt/tutorial/go.mod b/_examples/auth/jwt/tutorial/go.mod new file mode 100644 index 00000000..99e567cc --- /dev/null +++ b/_examples/auth/jwt/tutorial/go.mod @@ -0,0 +1,10 @@ +module myapp + +go 1.15 + +require ( + github.com/kataras/iris/v12 v12.2.0-alpha.0.20201031040657-23d4c411cadb + github.com/google/uuid v1.1.2 +) + +replace github.com/kataras/iris/v12 => ../../../../ diff --git a/_examples/auth/jwt/tutorial/main.go b/_examples/auth/jwt/tutorial/main.go new file mode 100644 index 00000000..2dee4c00 --- /dev/null +++ b/_examples/auth/jwt/tutorial/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "time" + + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/middleware/jwt" + "github.com/kataras/iris/v12/middleware/jwt/blocklist/redis" + + // Optionally to set token identifier. + "github.com/google/uuid" +) + +var ( + signatureSharedKey = []byte("sercrethatmaycontainch@r32length") + + signer = jwt.NewSigner(jwt.HS256, signatureSharedKey, 15*time.Minute) + verifier = jwt.NewVerifier(jwt.HS256, signatureSharedKey) +) + +func main() { + app := iris.New() + + blocklist := redis.NewBlocklist() + verifier.Blocklist = blocklist + verifyMiddleware := verifier.Verify(func() interface{} { + return new(userClaims) + }) + + app.Get("/", loginView) + + api := app.Party("/api") + { + api.Post("/login", login) + api.Post("/logout", verifyMiddleware, logout) + + todoAPI := api.Party("/todos", verifyMiddleware) + { + todoAPI.Post("/", createTodo) + todoAPI.Get("/", listTodos) + todoAPI.Get("/{id:uint64}", getTodo) + } + } + + protectedAPI := app.Party("/protected", verifyMiddleware) + protectedAPI.Get("/", protected) + protectedAPI.Get("/logout", logout) + + // GET http://localhost:8080 + // POST http://localhost:8080/api/login + // POST http://localhost:8080/api/logout + // POST http://localhost:8080/api/todos + // GET http://localhost:8080/api/todos + // GET http://localhost:8080/api/todos/{id} + app.Listen(":8080") +} + +func authenticate(ctx iris.Context) { + claims := userClaims{ + Username: "kataras", + } + + // Generate JWT ID. + random, err := uuid.NewRandom() + if err != nil { + ctx.StopWithError(iris.StatusInternalServerError, err) + return + } + id := random.String() + + // Set the ID with the jwt.ID. + token, err := signer.Sign(claims, jwt.ID(id)) + + if err != nil { + ctx.StopWithError(iris.StatusInternalServerError, err) + return + } + + ctx.Write(token) +} + +func protected(ctx iris.Context) { + claims := jwt.Get(ctx).(*userClaims) + + // To the standard claims, e.g. the generated ID: + // jwt.GetVerifiedToken(ctx).StandardClaims.ID + + ctx.WriteString(claims.Username) +} diff --git a/_examples/auth/jwt/tutorial/user.go b/_examples/auth/jwt/tutorial/user.go new file mode 100644 index 00000000..3f9bb9b1 --- /dev/null +++ b/_examples/auth/jwt/tutorial/user.go @@ -0,0 +1,63 @@ +package main + +import "golang.org/x/crypto/bcrypt" + +func init() { + generateSampleUsers() +} + +// User represents our User model. +type User struct { + ID uint64 `json:"id"` + Username string `json:"username"` + HashedPassword []byte `json:"-"` +} + +// Users represents a user database. +// For the sake of the tutorial we use a simple slice of users. +var Users []User + +func generateSampleUsers() { + Users = []User{ + {ID: 1, Username: "vasiliki", HashedPassword: mustGeneratePassword("vasiliki_pass")}, // my grandmother. + {ID: 2, Username: "kataras", HashedPassword: mustGeneratePassword("kataras_pass")}, // me. + {ID: 3, Username: "george", HashedPassword: mustGeneratePassword("george_pass")}, // my young brother. + {ID: 4, Username: "kwstas", HashedPassword: mustGeneratePassword("kwstas_pass")}, // my youngest brother. + } +} + +func fetchUser(username, password string) (User, bool) { + for _, u := range Users { // our example uses a static slice. + if u.Username == username { + // we compare the user input and the stored hashed password. + ok := ValidatePassword(password, u.HashedPassword) + if ok { + return u, true + } + } + } + + return User{}, false +} + +// mustGeneratePassword same as GeneratePassword but panics on errors. +func mustGeneratePassword(userPassword string) []byte { + hashed, err := GeneratePassword(userPassword) + if err != nil { + panic(err) + } + + return hashed +} + +// GeneratePassword will generate a hashed password for us based on the +// user's input. +func GeneratePassword(userPassword string) ([]byte, error) { + return bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost) +} + +// ValidatePassword will check if passwords are matched. +func ValidatePassword(userPassword string, hashed []byte) bool { + err := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword)) + return err == nil +} diff --git a/_examples/sessions/viewdata/main.go b/_examples/sessions/viewdata/main.go new file mode 100644 index 00000000..44aae9c1 --- /dev/null +++ b/_examples/sessions/viewdata/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/sessions" +) + +func main() { + app := iris.New() + app.RegisterView(iris.HTML("./views", ".html")) + + sess := sessions.New(sessions.Config{Cookie: "session_cookie", AllowReclaim: true}) + app.Use(sess.Handler()) + // ^ use app.UseRouter instead to access sessions on HTTP errors too. + + // Register our custom middleware, after the sessions middleware. + app.Use(setSessionViewData) + + app.Get("/", index) + app.Listen(":8080") +} + +func setSessionViewData(ctx iris.Context) { + session := sessions.Get(ctx) + ctx.ViewData("session", session) + ctx.Next() +} + +func index(ctx iris.Context) { + session := sessions.Get(ctx) + session.Set("username", "kataras") + ctx.View("index") + /* OR without middleware: + ctx.View("index", iris.Map{ + "session": session, + // {{.session.Get "username"}} + // OR to pass only the 'username': + // "username": session.Get("username"), + // {{.username}} + }) + */ +} diff --git a/_examples/sessions/viewdata/views/index.html b/_examples/sessions/viewdata/views/index.html new file mode 100644 index 00000000..f93e5a38 --- /dev/null +++ b/_examples/sessions/viewdata/views/index.html @@ -0,0 +1,11 @@ + + + + + + Sessions View Data + + + Hello {{.session.Get "username"}} + + \ No newline at end of file diff --git a/middleware/jwt/blocklist/redis/blocklist.go b/middleware/jwt/blocklist/redis/blocklist.go index 831429ef..74b63cd8 100644 --- a/middleware/jwt/blocklist/redis/blocklist.go +++ b/middleware/jwt/blocklist/redis/blocklist.go @@ -4,7 +4,6 @@ import ( "context" "io" "sync/atomic" - "time" "github.com/kataras/iris/v12/core/host" "github.com/kataras/iris/v12/middleware/jwt" @@ -30,7 +29,6 @@ type Client interface { // Blocklist is a jwt.Blocklist backed by Redis. type Blocklist struct { - Clock func() time.Time // Required. Defaults to time.Now. // GetKey is a function which can be used how to extract // the unique identifier for a token. // Required. By default the token key is extracted through the claims.ID ("jti"). @@ -67,7 +65,6 @@ var _ jwt.Blocklist = (*Blocklist)(nil) // verifier.Blocklist = blocklist func NewBlocklist() *Blocklist { return &Blocklist{ - Clock: time.Now, GetKey: defaultGetKey, Prefix: "", ClientOptions: Options{