jwt for webapp
This commit is contained in:
parent
7e77fd7d27
commit
9d332bbda4
5 changed files with 112 additions and 152 deletions
|
|
@ -3,28 +3,42 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"git.giftfish.de/ston1th/gowiki/pkg/log"
|
||||
"git.giftfish.de/ston1th/jwt"
|
||||
"github.com/gorilla/mux"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) *Context {
|
||||
func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) (ctx *Context) {
|
||||
h := w.Header()
|
||||
h.Set("X-Frame-Options", "DENY")
|
||||
h.Set("X-Content-Type-Options", "nosniff")
|
||||
h.Set("X-XSS-Protection", "1; mode=block")
|
||||
h.Set("Content-Security-Policy", "default-src 'none';style-src 'self' 'unsafe-inline';frame-ancestors 'none'")
|
||||
return &Context{
|
||||
ctx = &Context{
|
||||
Request: r,
|
||||
Response: w,
|
||||
Srv: s,
|
||||
Time: time.Now(),
|
||||
}
|
||||
if c, err := r.Cookie(cookieName); err == nil {
|
||||
t, err := jwt.DecodeToken(c.Value)
|
||||
if err != nil {
|
||||
log.Println("DecodeToken:", err)
|
||||
} else {
|
||||
if err = s.JWT.Verify(t); err != nil {
|
||||
log.Println("VerifyToken:", err)
|
||||
} else {
|
||||
ctx.Token = *t
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.SetCookie(nil)
|
||||
return
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
|
|
@ -36,6 +50,8 @@ type Context struct {
|
|||
Err error
|
||||
Time time.Time
|
||||
|
||||
Token jwt.Token
|
||||
|
||||
Data webData
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +80,8 @@ func (c *Context) Exec() {
|
|||
c.Err = errors.New("template is nil")
|
||||
return
|
||||
}
|
||||
c.newToken()
|
||||
c.Data.Token = newXsrf(c.Token.RawSig()[:keySize])
|
||||
|
||||
c.Data.Version = c.Srv.Version
|
||||
if c.Data.BodyTitle == "" {
|
||||
c.Data.BodyTitle = c.Data.Title
|
||||
|
|
@ -139,111 +156,54 @@ func (c *Context) Var(name string) (ret string) {
|
|||
return
|
||||
}
|
||||
|
||||
func (c *Context) initToken() {
|
||||
token := c.getToken()
|
||||
if token == nil || len(token) != keyLen {
|
||||
s, _ := c.Srv.Store.Get(c.Request, cookieName)
|
||||
s.Values["token"] = genKey(keyLen)
|
||||
s.Options = c.Srv.Store.Options
|
||||
err := s.Save(c.Request, c.Response)
|
||||
if err != nil {
|
||||
log.Println("InitToken: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) newToken() {
|
||||
c.initToken()
|
||||
rnd := genKey(keyLen)
|
||||
c.Data.Token = base64.StdEncoding.EncodeToString(append(rnd, xor(rnd, c.getToken())...))
|
||||
}
|
||||
|
||||
// CheckToken validates the xsrf token
|
||||
func (c *Context) CheckToken(f func()) bool {
|
||||
token, err := base64.StdEncoding.DecodeString(c.Form("token"))
|
||||
if err != nil {
|
||||
log.Println("CheckToken: ", err)
|
||||
return false
|
||||
}
|
||||
if len(token) != authLen {
|
||||
return false
|
||||
}
|
||||
ok := subtle.ConstantTimeCompare(c.getToken(), xor(token[keyLen:], token[:keyLen])) == 1
|
||||
func (c *Context) CheckXsrf(f func()) (ok bool) {
|
||||
ok = checkXsrf(c.Form("token"), c.Token.RawSig()[:keySize])
|
||||
if !ok {
|
||||
if f != nil {
|
||||
f()
|
||||
return ok
|
||||
return
|
||||
}
|
||||
c.Data.Msg = "wrong csrf token"
|
||||
c.Exec()
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c *Context) LoggedOn() (ok bool) {
|
||||
s, _ := c.Srv.Store.Get(c.Request, cookieName)
|
||||
_, ok = s.Values["user"]
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Context) SetUdata(user string, admin bool) {
|
||||
s, _ := c.Srv.Store.Get(c.Request, cookieName)
|
||||
s.Values["user"] = user
|
||||
s.Values["admin"] = admin
|
||||
s.Options = c.Srv.Store.Options
|
||||
if err := s.Save(c.Request, c.Response); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
func (c *Context) LoggedOn() (ok bool) {
|
||||
_, ok = c.Token.Claims["user"]
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Context) UnsetUdata() {
|
||||
s, _ := c.Srv.Store.Get(c.Request, cookieName)
|
||||
s.Values = make(map[interface{}]interface{})
|
||||
s.Options = c.Srv.Store.Options
|
||||
if err := s.Save(c.Request, c.Response); err != nil {
|
||||
func (c *Context) SetCookie(claims map[string]interface{}) {
|
||||
t := jwt.NewToken(claims, nil)
|
||||
if err := c.Srv.JWT.Sign(t); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
c.Token = *t
|
||||
http.SetCookie(c.Response, &http.Cookie{
|
||||
Name: cookieName,
|
||||
Value: t.Sig(),
|
||||
MaxAge: 3600 * 12,
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Context) GetUser() string {
|
||||
s, _ := c.Srv.Store.Get(c.Request, cookieName)
|
||||
if ret, ok := s.Values["user"].(string); ok {
|
||||
if ret, ok := c.Token.Claims["user"].(string); ok {
|
||||
return ret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *Context) GetAdmin() bool {
|
||||
s, _ := c.Srv.Store.Get(c.Request, cookieName)
|
||||
if ret, ok := s.Values["admin"].(bool); ok {
|
||||
if ret, ok := c.Token.Claims["admin"].(bool); ok {
|
||||
return ret
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Context) getToken() []byte {
|
||||
s, _ := c.Srv.Store.Get(c.Request, cookieName)
|
||||
t, ok := s.Values["token"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
b, ok := t.([]byte)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func xor(a, b []byte) (c []byte) {
|
||||
n := len(a)
|
||||
if len(b) != n {
|
||||
return
|
||||
}
|
||||
c = make([]byte, n)
|
||||
for i := 0; i < n; i++ {
|
||||
c[i] = a[i] ^ b[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ctxHandler func(*Context)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue