jwt.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // Copyright 2017 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package auth
  15. import (
  16. "context"
  17. "crypto/rsa"
  18. "io/ioutil"
  19. "time"
  20. jwt "github.com/dgrijalva/jwt-go"
  21. )
  22. type tokenJWT struct {
  23. signMethod string
  24. signKey *rsa.PrivateKey
  25. verifyKey *rsa.PublicKey
  26. ttl time.Duration
  27. }
  28. func (t *tokenJWT) enable() {}
  29. func (t *tokenJWT) disable() {}
  30. func (t *tokenJWT) invalidateUser(string) {}
  31. func (t *tokenJWT) genTokenPrefix() (string, error) { return "", nil }
  32. func (t *tokenJWT) info(ctx context.Context, token string, rev uint64) (*AuthInfo, bool) {
  33. // rev isn't used in JWT, it is only used in simple token
  34. var (
  35. username string
  36. revision uint64
  37. )
  38. parsed, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
  39. return t.verifyKey, nil
  40. })
  41. switch err.(type) {
  42. case nil:
  43. if !parsed.Valid {
  44. plog.Warningf("invalid jwt token: %s", token)
  45. return nil, false
  46. }
  47. claims := parsed.Claims.(jwt.MapClaims)
  48. username = claims["username"].(string)
  49. revision = uint64(claims["revision"].(float64))
  50. default:
  51. plog.Warningf("failed to parse jwt token: %s", err)
  52. return nil, false
  53. }
  54. return &AuthInfo{Username: username, Revision: revision}, true
  55. }
  56. func (t *tokenJWT) assign(ctx context.Context, username string, revision uint64) (string, error) {
  57. // Future work: let a jwt token include permission information would be useful for
  58. // permission checking in proxy side.
  59. tk := jwt.NewWithClaims(jwt.GetSigningMethod(t.signMethod),
  60. jwt.MapClaims{
  61. "username": username,
  62. "revision": revision,
  63. "exp": time.Now().Add(t.ttl).Unix(),
  64. })
  65. token, err := tk.SignedString(t.signKey)
  66. if err != nil {
  67. plog.Debugf("failed to sign jwt token: %s", err)
  68. return "", err
  69. }
  70. plog.Debugf("jwt token: %s", token)
  71. return token, err
  72. }
  73. func prepareOpts(opts map[string]string) (jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath string, ttl time.Duration, err error) {
  74. for k, v := range opts {
  75. switch k {
  76. case "sign-method":
  77. jwtSignMethod = v
  78. case "pub-key":
  79. jwtPubKeyPath = v
  80. case "priv-key":
  81. jwtPrivKeyPath = v
  82. case "ttl":
  83. ttl, err = time.ParseDuration(v)
  84. if err != nil {
  85. plog.Errorf("failed to parse ttl option (%s)", err)
  86. return "", "", "", 0, ErrInvalidAuthOpts
  87. }
  88. default:
  89. plog.Errorf("unknown token specific option: %s", k)
  90. return "", "", "", 0, ErrInvalidAuthOpts
  91. }
  92. }
  93. if len(jwtSignMethod) == 0 {
  94. return "", "", "", 0, ErrInvalidAuthOpts
  95. }
  96. return jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath, ttl, nil
  97. }
  98. func newTokenProviderJWT(opts map[string]string) (*tokenJWT, error) {
  99. jwtSignMethod, jwtPubKeyPath, jwtPrivKeyPath, ttl, err := prepareOpts(opts)
  100. if err != nil {
  101. return nil, ErrInvalidAuthOpts
  102. }
  103. if ttl == 0 {
  104. ttl = 5 * time.Minute
  105. }
  106. t := &tokenJWT{
  107. ttl: ttl,
  108. }
  109. t.signMethod = jwtSignMethod
  110. verifyBytes, err := ioutil.ReadFile(jwtPubKeyPath)
  111. if err != nil {
  112. plog.Errorf("failed to read public key (%s) for jwt: %s", jwtPubKeyPath, err)
  113. return nil, err
  114. }
  115. t.verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
  116. if err != nil {
  117. plog.Errorf("failed to parse public key (%s): %s", jwtPubKeyPath, err)
  118. return nil, err
  119. }
  120. signBytes, err := ioutil.ReadFile(jwtPrivKeyPath)
  121. if err != nil {
  122. plog.Errorf("failed to read private key (%s) for jwt: %s", jwtPrivKeyPath, err)
  123. return nil, err
  124. }
  125. t.signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)
  126. if err != nil {
  127. plog.Errorf("failed to parse private key (%s): %s", jwtPrivKeyPath, err)
  128. return nil, err
  129. }
  130. return t, nil
  131. }