// NewToken creates new JWT token for the gien username. It embedds the given // public key as kontrolKey and signs the token with the private one. func NewToken(username, private, public string) *jwt.Token { tknID := uuid.NewV4() hostname, err := os.Hostname() if err != nil { panic(err) } if username == "" { username = "******" } if testuser := os.Getenv("TESTKEY_USERNAME"); testuser != "" { username = testuser } claims := &kitekey.KiteClaims{ StandardClaims: jwt.StandardClaims{ Issuer: "testuser", Subject: username, Audience: hostname, IssuedAt: time.Now().UTC().Unix(), Id: tknID.String(), }, KontrolKey: public, KontrolURL: "http://localhost:4000/kite", } token := jwt.NewWithClaims(jwt.GetSigningMethod("RS256"), claims) rsaPrivate, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(private)) if err != nil { panic(err) } token.Raw, err = token.SignedString(rsaPrivate) if err != nil { panic(err) } // verify the token _, err = jwt.ParseWithClaims(token.Raw, claims, func(*jwt.Token) (interface{}, error) { return jwt.ParseRSAPublicKeyFromPEM([]byte(public)) }) if err != nil { panic(err) } token.Valid = true return token }
func (k *Kontrol) HandleGetKey(r *kite.Request) (interface{}, error) { // Only accept requests with kiteKey because we need this info // for checking if the key is valid and needs to be regenerated if r.Auth.Type != "kiteKey" { return nil, fmt.Errorf("Unexpected authentication type: %s", r.Auth.Type) } ex := &kitekey.Extractor{ Claims: &kitekey.KiteClaims{}, } if _, err := jwt.ParseWithClaims(r.Auth.Key, ex.Claims, ex.Extract); err != nil { return nil, err } if ex.Claims.KontrolKey == "" { return nil, errors.New("public key is not passed") } switch k.keyPair.IsValid(ex.Claims.KontrolKey) { case nil: // everything is ok, just return the old one return ex.Claims.KontrolKey, nil case ErrKeyDeleted: // client is using old key, update to current if kp, err := k.KeyPair(); err == nil { return kp.Public, nil } } keyPair, err := k.pickKey(r) if err != nil { return nil, err } return keyPair.Public, nil }
// Auth is a gin middleware that checks for session cookie func Auth() gin.HandlerFunc { return func(c *gin.Context) { // get the jwt cookie from the request cookie, err := c.Request.Cookie(u.CookieName) if err != nil { c.Error(err).SetMeta("middleware.Auth.Cookie") c.Redirect(http.StatusFound, "/admin/login") c.Abort() return } token, err := jwt.ParseWithClaims(cookie.Value, &u.TokenClaims{}, func(token *jwt.Token) (interface{}, error) { return u.ValidateToken(token) }) // the client side should delete any saved JWT tokens on unauth error if err != nil || !token.Valid { // delete the cookie http.SetCookie(c.Writer, u.DeleteCookie()) c.Error(err).SetMeta("middleware.Auth.ParseWithClaims") c.Redirect(http.StatusFound, "/admin/login") c.Abort() return } // set user data for controllers c.Set("authenticated", true) c.Next() } }
// Auth is a gin middleware that checks for session cookie and // handles permissions func Auth(authenticated bool) gin.HandlerFunc { return func(c *gin.Context) { // error if theres no secret set if Secret == "" { c.JSON(e.ErrorMessage(e.ErrInternalError)) c.Error(e.ErrNoSecret).SetMeta("auth.Auth") c.Abort() return } // set default anonymous user user := DefaultUser() // try and get the jwt cookie from the request cookie, err := c.Request.Cookie(CookieName) // parse jwt token if its there if err != http.ErrNoCookie { token, err := jwt.ParseWithClaims(cookie.Value, &TokenClaims{}, func(token *jwt.Token) (interface{}, error) { return validateToken(token, &user) }) // if theres some jwt error other than no token in request or the token is // invalid then return unauth // the client side should delete any saved JWT tokens on unauth error if err != nil || !token.Valid { // delete the cookie http.SetCookie(c.Writer, DeleteCookie()) c.JSON(e.ErrorMessage(e.ErrUnauthorized)) c.Error(err).SetMeta("user.Auth") c.Abort() return } } // check if user needed to be authenticated // this needs to be like this for routes that dont need auth // if we just check equality then logged in users wont be able // to view anon pages ;P if authenticated && !user.IsAuthenticated { c.JSON(e.ErrorMessage(e.ErrForbidden)) c.Error(e.ErrForbidden).SetMeta("user.Auth") c.Abort() return } // set user data for controllers c.Set("userdata", user) c.Next() } }
func parseToken(tokenString string, publicKey *rsa.PublicKey) (*keycloakTokenClaims, error) { token, err := jwt.ParseWithClaims(tokenString, &keycloakTokenClaims{}, func(t *jwt.Token) (interface{}, error) { return publicKey, nil }) if err != nil { return nil, err } claims := token.Claims.(*keycloakTokenClaims) if token.Valid { return claims, nil } return nil, errs.WithStack(errors.New("token is not valid")) }
// KeyPair looks up a key pair that was used to sign Kontrol's kite key. // // The value is cached on first call of the function. func (k *Kontrol) KeyPair() (pair *KeyPair, err error) { if k.selfKeyPair != nil { return k.selfKeyPair, nil } kiteKey := k.Kite.KiteKey() if kiteKey == "" || len(k.lastPublic) == 0 { return nil, errNoSelfKeyPair } keyIndex := -1 me := new(multiError) for i := range k.lastPublic { ri := len(k.lastPublic) - i - 1 keyFn := func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { return nil, errors.New("invalid signing method") } return jwt.ParseRSAPublicKeyFromPEM([]byte(k.lastPublic[ri])) } if _, err := jwt.ParseWithClaims(kiteKey, &kitekey.KiteClaims{}, keyFn); err != nil { me.err = append(me.err, err) continue } keyIndex = ri break } if keyIndex == -1 { return nil, fmt.Errorf("no matching self key pair found: %s", me) } k.selfKeyPair = &KeyPair{ ID: k.lastIDs[keyIndex], Public: k.lastPublic[keyIndex], Private: k.lastPrivate[keyIndex], } return k.selfKeyPair, nil }
// parse the token string and set func (t *TokenRenewer) parse(tokenString string) error { claims := &kitekey.KiteClaims{} _, err := jwt.ParseWithClaims(tokenString, claims, t.localKite.RSAKey) if err != nil { valErr, ok := err.(*jwt.ValidationError) if !ok { return err } // do noy return for ValidationErrorSignatureValid. This is because we // might asked for a kite who's public Key is different what we have. // We still should be able to send them requests. if (valErr.Errors & jwt.ValidationErrorSignatureInvalid) == 0 { return fmt.Errorf("Cannot parse token: %s", err) } } t.validUntil = time.Unix(claims.ExpiresAt, 0).UTC() return nil }
func (k *Kite) updateAuth(reg *protocol.RegisterResult) { k.configMu.Lock() defer k.configMu.Unlock() switch { case reg.KiteKey != "": k.Config.KiteKey = reg.KiteKey ex := &kitekey.Extractor{ Claims: &kitekey.KiteClaims{}, } if _, err := jwt.ParseWithClaims(reg.KiteKey, ex.Claims, ex.Extract); err != nil { k.Log.Error("auth update: unable to extract kontrol key: %s", err) break } if ex.Claims.KontrolKey != "" { reg.PublicKey = ex.Claims.KontrolKey } } // we also received a new public key (means the old one was invalidated). // Use it now. if reg.PublicKey != "" { k.Config.KontrolKey = reg.PublicKey key, err := jwt.ParseRSAPublicKeyFromPEM([]byte(reg.PublicKey)) if err != nil { k.Log.Error("auth update: unable to update kontrol key: %s", err) return } k.kontrolKey = key } }
func TestCapability(t *testing.T) { t.Parallel() cap := NewCapability("AC123", "123") cap.AllowClientIncoming("client-name") tok, err := cap.GenerateToken(time.Hour) if err != nil { t.Fatal(err) } fmt.Println(tok) cc := new(customClaim) _, err = jwt.ParseWithClaims(tok, cc, func(tkn *jwt.Token) (interface{}, error) { return []byte("123"), nil }) if err != nil { t.Fatal(err) } if cc.StandardClaims.Issuer != "AC123" { t.Errorf("bad Issuer") } if cc.Scope != "scope:client:incoming?clientName=client-name" { t.Errorf("bad Scope") } }
func (k *Konfig) buildKiteConfig() *konfig.Config { if k.KiteKey != "" { tok, err := jwt.ParseWithClaims(k.KiteKey, &kitekey.KiteClaims{}, kitekey.GetKontrolKey) if err == nil { cfg := &konfig.Config{} if err = cfg.ReadToken(tok); err == nil { return cfg } } } if k.KiteKeyFile != "" { if cfg, err := konfig.NewFromKiteKey(k.KiteKeyFile); err == nil { return cfg } } if cfg, err := konfig.Get(); err == nil { return cfg } return konfig.New() }
// Midware handles token authentication for external authentication // sources. func Midware(publicKeyBase64Str string, config MidwareOpts) (web.Middleware, error) { publicKey, err := auth.DecodePublicKey(publicKeyBase64Str) if err != nil { log.Error("startup", "auth : Midware", err, "Can not decode the public key base64 encoding") return nil, err } // Create the middleware to actually return. m := func(h web.Handler) web.Handler { // Create the handler that we should return as a part of the middleware // chain. f := func(c *web.Context) error { log.Dev(c.SessionID, "auth : Midware", "Started") // Extract the token from the Authorization header provided on the request. tokenString := c.Request.Header.Get("Authorization") // In the event that the request does not have a header key for the // Authorization header, and we are allowed to check the query string, then // we need to try and access it from the URL query parameters. if tokenString == "" && config.AllowQueryString { tokenString = c.Request.URL.Query().Get("access_token") } if tokenString == "" { log.Error(c.SessionID, "auth : Midware", ErrInvalidToken, "No token on request") return web.ErrNotAuthorized } // This describes the key validation function to provide the certificate // to validate the signature on the passed in JWT. keyValidation := func(token *jwt.Token) (interface{}, error) { // Don't forget to validate the alg is what you expect. if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok { return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) } // Return with the public key that was provided in the config. return publicKey, nil } // Here we actually parse/verify the signature on the JWT and extract the // claims. token, err := jwt.ParseWithClaims(tokenString, &jwt.MapClaims{}, keyValidation) if err != nil { log.Error(c.SessionID, "auth : Midware", err, "Token could not be parsed") return web.ErrNotAuthorized } // Return with an error if the token is not valid. if !token.Valid { log.Error(c.SessionID, "auth : Midware", ErrInvalidToken, "Token not valid") return web.ErrNotAuthorized } // Ensure that the claims that are inside the token are indeed the MapClaims // that we expect. claims, ok := token.Claims.(*jwt.MapClaims) if !ok { log.Error(c.SessionID, "auth : Midware", ErrInvalidToken, "Claims not valid") return web.ErrNotAuthorized } // Validate that all the parameters we expect are correct, noteably, the // expiry date, and not before claims should be verified. if err := claims.Valid(); err != nil { log.Error(c.SessionID, "auth : Midware", err, "Claims not valid") return web.ErrNotAuthorized } // Add the claims to the context. c.Ctx["claims"] = claims log.Dev(c.SessionID, "auth : Midware", "Completed : Valid") return h(c) } return f } return m, nil }
func TestJWT(t *testing.T) { t.Parallel() accTkn := New(ACC_SID, API_KEY, API_SECRET, IDENTITY, time.Hour) accTkn.NotBefore = time.Now() convGrant := NewConversationsGrant(APP_SID) accTkn.AddGrant(convGrant) jwtString, err := accTkn.JWT() if err != nil { t.Error("Unexpected error when generating the token", err) } if jwtString == "" { t.Error("token returned is empty") } token, err := jwt.ParseWithClaims(jwtString, &myCustomClaims{}, func(tkn *jwt.Token) (interface{}, error) { return []byte(API_SECRET), nil }) if err != nil { t.Error("Unexpected error when generating the token", err) } claims := token.Claims.(*myCustomClaims) if &claims.StandardClaims == nil { t.Error("Claim doesn't conaint a standard claims struct") } if claims.StandardClaims.ExpiresAt == 0 { t.Error("ExpiredAt is not set") } if claims.StandardClaims.Id == "" { t.Error("ID is not set") } if claims.StandardClaims.IssuedAt == 0 { t.Error("IssuedAt is not set") } if claims.StandardClaims.NotBefore == 0 { t.Error("NotBefore is not set") } if claims.StandardClaims.Issuer != API_KEY { t.Errorf("Issuer expected to be: %s, got %s\n", API_KEY, claims.StandardClaims.Issuer) } if claims.StandardClaims.Subject != ACC_SID { t.Errorf("Subject expected to be: %s, got %s\n", ACC_SID, claims.StandardClaims.Subject) } if claims.Grants == nil { t.Error("Expected Grants to exist") } if claims.Grants["identity"] != IDENTITY { t.Errorf("Grants identity expected to be %s, got %s\n", IDENTITY, claims.Grants["identity"]) } }
func (k *Kontrol) HandleRegisterHTTP(rw http.ResponseWriter, req *http.Request) { var args protocol.RegisterArgs if err := json.NewDecoder(req.Body).Decode(&args); err != nil { errMsg := fmt.Errorf("wrong register input: '%s'", err) http.Error(rw, jsonError(errMsg), http.StatusBadRequest) return } k.log.Info("Register (via HTTP) request from: %s", args.Kite) // Only accept requests with kiteKey, because that's the only way one can // register itself to kontrol. if args.Auth.Type != "kiteKey" { err := fmt.Errorf("unexpected authentication type: %s", args.Auth.Type) http.Error(rw, jsonError(err), http.StatusBadRequest) return } // empty url is useless for us if args.URL == "" { err := errors.New("empty URL") http.Error(rw, jsonError(err), http.StatusBadRequest) return } // decode and authenticated the token key. We'll get the authenticated // username username, err := k.Kite.AuthenticateSimpleKiteKey(args.Auth.Key) if err != nil { http.Error(rw, jsonError(err), http.StatusUnauthorized) return } args.Kite.Username = username ex := &kitekey.Extractor{ Claims: &kitekey.KiteClaims{}, } t, err := jwt.ParseWithClaims(args.Auth.Key, ex.Claims, ex.Extract) if err != nil { http.Error(rw, jsonError(err), http.StatusBadRequest) return } var keyPair *KeyPair resp := &protocol.RegisterResult{ URL: args.URL, HeartbeatInterval: int64(HeartbeatInterval / time.Second), } // check if the key is valid and is stored in the key pair storage, if not // found we don't allow to register anyone. r := &kite.Request{ Username: username, Auth: &kite.Auth{ Type: args.Auth.Type, Key: args.Auth.Key, }, } keyPair, resp.KiteKey, err = k.getOrUpdateKeyPub(ex.Claims.KontrolKey, t, r) if err != nil { http.Error(rw, jsonError(err), http.StatusBadRequest) return } if ex.Claims.KontrolKey != keyPair.Public { // NOTE(rjeczalik): updates public key for old kites, new kites // expect kite key to be updated resp.PublicKey = keyPair.Public } remoteKite := args.Kite // Be sure we have a valid Kite representation. We should not allow someone // with an empty field to be registered. if err := validateKiteKey(remoteKite); err != nil { http.Error(rw, jsonError(err), http.StatusBadRequest) return } // This will be stored into the final storage value := &kontrolprotocol.RegisterValue{ URL: args.URL, KeyID: keyPair.ID, } // Register first by adding the value to the storage. Return if there is // any error. if err := k.storage.Upsert(remoteKite, value); err != nil { k.log.Error("storage add '%s' error: %s", remoteKite, err) http.Error(rw, jsonError(errors.New("internal error - register")), http.StatusInternalServerError) return } k.heartbeatsMu.Lock() defer k.heartbeatsMu.Unlock() h, ok := k.heartbeats[remoteKite.ID] if ok { // there is already a previous registration, use it k.log.Info("Kite was already register (via HTTP), use timer cache %s", remoteKite) h.timer.Reset(HeartbeatInterval + HeartbeatDelay) // update registerURL of the previously started heartbeat goroutine // so it does not get overwritten back to the old value h.updateC <- func() error { return k.storage.Update(remoteKite, value) } } else { // we create a new ticker which is going to update the key periodically in // the storage so it's always up to date. Instead of updating the key // periodically according to the HeartBeatInterval below, we are buffering // the write speed here with the UpdateInterval. h = &heartbeat{ updateC: make(chan func() error), } updater := time.NewTicker(UpdateInterval) go func() { update := func() error { return k.storage.Update(remoteKite, value) } for { select { case <-k.closed: return case <-updater.C: k.log.Debug("Kite is active (via HTTP), updating the value %s", remoteKite) if err := update(); err != nil { k.log.Error("storage update '%s' error: %s", remoteKite, err) } case fn, ok := <-h.updateC: if !ok { k.log.Info("Kite is nonactive (via HTTP). Updater is closed %s", remoteKite) return } update = fn } } }() // we are now creating a timer that is going to call the function which // stops the background updater if it's not resetted. The time is being // resetted on a separate HTTP endpoint "/heartbeat" h.timer = time.AfterFunc(HeartbeatInterval+HeartbeatDelay, func() { k.log.Info("Kite didn't sent any heartbeat (via HTTP). Stopping the updater %s", remoteKite) // stop the updater so it doesn't update it in the background updater.Stop() k.heartbeatsMu.Lock() defer k.heartbeatsMu.Unlock() select { case <-h.updateC: default: close(h.updateC) } delete(k.heartbeats, remoteKite.ID) }) k.heartbeats[remoteKite.ID] = h } k.log.Info("Kite registered (via HTTP): %s", remoteKite) // send the response back to the requester if err := json.NewEncoder(rw).Encode(resp); err != nil { errMsg := fmt.Errorf("could not encode response: '%s'", err) http.Error(rw, jsonError(errMsg), http.StatusInternalServerError) return } }
func (k *Kontrol) HandleRegister(r *kite.Request) (interface{}, error) { k.log.Info("Register request from: %s", r.Client.Kite) // Only accept requests with kiteKey because we need this info // for generating tokens for this kite. if r.Auth.Type != "kiteKey" { return nil, fmt.Errorf("Unexpected authentication type: %s", r.Auth.Type) } var args struct { URL string `json:"url"` } if err := r.Args.One().Unmarshal(&args); err != nil { return nil, err } if args.URL == "" { return nil, errors.New("empty url") } if _, err := url.Parse(args.URL); err != nil { return nil, fmt.Errorf("invalid register URL: %s", err) } res := &protocol.RegisterResult{ URL: args.URL, } ex := &kitekey.Extractor{ Claims: &kitekey.KiteClaims{}, } t, err := jwt.ParseWithClaims(r.Auth.Key, ex.Claims, ex.Extract) if err != nil { return nil, err } var keyPair *KeyPair var origKey = ex.Claims.KontrolKey // check if the key is valid and is stored in the key pair storage, if not // check if there is a new key we can use. keyPair, res.KiteKey, err = k.getOrUpdateKeyPub(ex.Claims.KontrolKey, t, r) if err != nil { return nil, err } if origKey != keyPair.Public { // NOTE(rjeczalik): updates public key for old kites, new kites // expect kite key to be updated res.PublicKey = keyPair.Public } if err := validateKiteKey(&r.Client.Kite); err != nil { return nil, err } value := &kontrolprotocol.RegisterValue{ URL: args.URL, KeyID: keyPair.ID, } // Register first by adding the value to the storage. Return if there is // any error. if err := k.storage.Upsert(&r.Client.Kite, value); err != nil { k.log.Error("storage add '%s' error: %s", &r.Client.Kite, err) return nil, errors.New("internal error - register") } every := onceevery.New(UpdateInterval) ping := make(chan struct{}, 1) closed := int32(0) kiteCopy := r.Client.Kite updaterFunc := func() { for { select { case <-k.closed: return case <-ping: k.log.Debug("Kite is active, got a ping %s", &kiteCopy) every.Do(func() { k.log.Debug("Kite is active, updating the value %s", &kiteCopy) err := k.storage.Update(&kiteCopy, value) if err != nil { k.log.Error("storage update '%s' error: %s", &kiteCopy, err) } }) case <-time.After(HeartbeatInterval + HeartbeatDelay): k.log.Debug("Kite didn't sent any heartbeat %s.", &kiteCopy) atomic.StoreInt32(&closed, 1) return } } } go updaterFunc() heartbeatArgs := []interface{}{ HeartbeatInterval / time.Second, dnode.Callback(func(args *dnode.Partial) { k.log.Debug("Kite send us an heartbeat. %s", &kiteCopy) k.clientLocks.Get(kiteCopy.ID).Lock() defer k.clientLocks.Get(kiteCopy.ID).Unlock() select { case ping <- struct{}{}: default: } // seems we miss a heartbeat, so start it again! if atomic.CompareAndSwapInt32(&closed, 1, 0) { k.log.Warning("Updater was closed, but we are still getting heartbeats. Starting again %s", &kiteCopy) // it might be removed because the ttl cleaner would come // before us, so try to add it again, the updater will than // continue to update it afterwards. k.storage.Upsert(&kiteCopy, value) go updaterFunc() } }), } // now trigger the remote kite so it sends us periodically an heartbeat resp := r.Client.GoWithTimeout("kite.heartbeat", 4*time.Second, heartbeatArgs...) go func() { if err := (<-resp).Err; err != nil { k.log.Error("failed requesting heartbeats from %q kite: %s", kiteCopy.Name, err) } }() k.log.Info("Kite registered: %s", &r.Client.Kite) clientKite := r.Client.Kite.String() r.Client.OnDisconnect(func() { k.log.Info("Kite disconnected: %s", clientKite) }) return res, nil }