firejwt 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e9f92d3a9e3bc19d0f29b167acfb4e9c1101a3dc8186b9ea33fd9327de25dc0b
4
- data.tar.gz: 13bda374efe299021fc0778add9d58fa850f95f22021f18b0ed858e07cb03d43
3
+ metadata.gz: ab69a025ab89bc78cc7100320e86215b969e65b861321e8166ff0facf2aca0c7
4
+ data.tar.gz: 566c9ec8876efdde35f727a61c2b6ffc87203c241bd287ee5dbea302f35a5d5b
5
5
  SHA512:
6
- metadata.gz: 311080f5997340532acfb2d98f42090d18d3a9aa039c72d17cadf756c8e51aea2b46ee3ba83aaf30e61c3db8e3c9022067f8e822586f3d963688f1321a67fc0e
7
- data.tar.gz: 4f13449932fc23d94c211eea7e7bc2a8ddf6615987d6e315d92a54a1efc415b6546f959f461865f5780f8f98c679dd54d2a428d40016dd79f81440c05921da1c
6
+ metadata.gz: 254b2653d48d41c64b4837e044a19f2412ffd93a019417c1ddf050b715643c0502b93a11439cefde9e13f9ab7416eb5b059f57a4ca9b8b080ad4ad4e7780be74
7
+ data.tar.gz: 44eb4c65193a62c0a3ae38d2c80898c623168f267caa74080260a9c37c59148a9918ec9e02371e6bb03358705873d5b8314f993cc2707692e5938a4de559f6bd
@@ -20,7 +20,7 @@ jobs:
20
20
  runs-on: ubuntu-latest
21
21
  strategy:
22
22
  matrix:
23
- go-version: [1.16.x, 1.17.x]
23
+ go-version: [1.18.x, 1.17.x]
24
24
  steps:
25
25
  - name: Checkout
26
26
  uses: actions/checkout@v2
@@ -41,7 +41,7 @@ jobs:
41
41
  runs-on: ubuntu-latest
42
42
  strategy:
43
43
  matrix:
44
- ruby-version: ["2.6", "2.7", "3.0", "3.1"]
44
+ ruby-version: ["2.7", "3.0", "3.1"]
45
45
  steps:
46
46
  - uses: actions/checkout@v2
47
47
  - uses: ruby/setup-ruby@v1
data/.rubocop.yml CHANGED
@@ -6,7 +6,7 @@ inherit_mode:
6
6
  - Exclude
7
7
 
8
8
  AllCops:
9
- TargetRubyVersion: "2.6"
9
+ TargetRubyVersion: "2.7"
10
10
 
11
11
  RSpec/FilePath:
12
12
  Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- firejwt (0.4.1)
4
+ firejwt (0.5.0)
5
5
  jwt
6
6
 
7
7
  GEM
@@ -16,7 +16,7 @@ GEM
16
16
  hashdiff (1.0.1)
17
17
  jwt (2.3.0)
18
18
  parallel (1.21.0)
19
- parser (3.1.0.0)
19
+ parser (3.1.1.0)
20
20
  ast (~> 2.4.1)
21
21
  public_suffix (4.0.6)
22
22
  rainbow (3.1.1)
@@ -36,28 +36,28 @@ GEM
36
36
  diff-lcs (>= 1.2.0, < 2.0)
37
37
  rspec-support (~> 3.11.0)
38
38
  rspec-support (3.11.0)
39
- rubocop (1.25.1)
39
+ rubocop (1.26.0)
40
40
  parallel (~> 1.10)
41
41
  parser (>= 3.1.0.0)
42
42
  rainbow (>= 2.2.2, < 4.0)
43
43
  regexp_parser (>= 1.8, < 3.0)
44
44
  rexml
45
- rubocop-ast (>= 1.15.1, < 2.0)
45
+ rubocop-ast (>= 1.16.0, < 2.0)
46
46
  ruby-progressbar (~> 1.7)
47
47
  unicode-display_width (>= 1.4.0, < 3.0)
48
- rubocop-ast (1.15.2)
49
- parser (>= 3.0.1.1)
48
+ rubocop-ast (1.16.0)
49
+ parser (>= 3.1.1.0)
50
50
  rubocop-bsm (0.6.0)
51
51
  rubocop (~> 1.0)
52
52
  rubocop-performance
53
53
  rubocop-rake
54
54
  rubocop-rspec
55
- rubocop-performance (1.13.2)
55
+ rubocop-performance (1.13.3)
56
56
  rubocop (>= 1.7.0, < 2.0)
57
57
  rubocop-ast (>= 0.4.0)
58
58
  rubocop-rake (0.6.0)
59
59
  rubocop (~> 1.0)
60
- rubocop-rspec (2.8.0)
60
+ rubocop-rspec (2.9.0)
61
61
  rubocop (~> 1.19)
62
62
  ruby-progressbar (1.11.0)
63
63
  unicode-display_width (2.1.0)
data/firejwt.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'firejwt'
3
- s.version = '0.4.1'
3
+ s.version = '0.5.0'
4
4
  s.authors = ['Black Square Media Ltd']
5
5
  s.email = ['info@blacksquaremedia.com']
6
6
  s.summary = %(Firebase JWT validation)
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^spec/}) }
12
12
  s.test_files = `git ls-files -z -- spec/*`.split("\x0")
13
13
  s.require_paths = ['lib']
14
- s.required_ruby_version = '>= 2.6'
14
+ s.required_ruby_version = '>= 2.7'
15
15
 
16
16
  s.add_dependency 'jwt'
17
17
  s.add_development_dependency 'rake'
data/firejwt.go CHANGED
@@ -16,6 +16,10 @@ import (
16
16
  "github.com/golang-jwt/jwt/v4"
17
17
  )
18
18
 
19
+ func init() {
20
+ jwt.MarshalSingleStringAsArray = false
21
+ }
22
+
19
23
  const defaultURL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"
20
24
 
21
25
  // Validator validates Firebase JWTs
@@ -99,11 +103,9 @@ func (v *Validator) Refresh() error {
99
103
  }
100
104
 
101
105
  var (
102
- errKIDMissing = errors.New("missing kid header")
103
- errExpired = errors.New("token has expired")
104
- errIssuedFuture = errors.New("issued in the future")
105
- errNoSubject = errors.New("subject is missing")
106
- errAuthFuture = errors.New("auth-time in the future")
106
+ errKIDMissing = errors.New("token is missing kid header")
107
+ errNoSubject = errors.New("token has no subject")
108
+ errAuthFuture = errors.New("token auth_time not valid")
107
109
  errTokenInvalid = errors.New("token is invalid")
108
110
  )
109
111
 
@@ -118,22 +120,10 @@ func (v *Validator) verify(token *jwt.Token) (interface{}, error) {
118
120
  return nil, fmt.Errorf("invalid kid header %q", kid)
119
121
  }
120
122
 
121
- now := time.Now().Unix()
122
123
  claims := token.Claims.(*Claims)
123
- if claims.Audience != v.audience {
124
- return nil, fmt.Errorf("invalid audience claim %q", claims.Audience)
125
- } else if claims.Issuer != v.issuer {
126
- return nil, fmt.Errorf("invalid issuer claim %q", claims.Issuer)
127
- } else if claims.Subject == "" {
128
- return nil, errNoSubject
129
- } else if claims.ExpiresAt <= now {
130
- return nil, errExpired
131
- } else if claims.IssuedAt > now {
132
- return nil, errIssuedFuture
133
- } else if claims.AuthAt > now {
134
- return nil, errAuthFuture
124
+ if err := claims.validate(time.Now(), v.audience, v.issuer); err != nil {
125
+ return nil, err
135
126
  }
136
-
137
127
  return key.PublicKey, nil
138
128
  }
139
129
 
@@ -188,19 +178,34 @@ func (k *publicKey) UnmarshalText(data []byte) error {
188
178
 
189
179
  // Claims are included in the token.
190
180
  type Claims struct {
191
- Subject string `json:"sub,omitempty"`
192
- Audience string `json:"aud,omitempty"`
193
- Issuer string `json:"iss,omitempty"`
194
- IssuedAt int64 `json:"iat,omitempty"`
195
- ExpiresAt int64 `json:"exp,omitempty"`
196
-
197
- Name string `json:"name,omitempty"`
198
- Picture string `json:"picture,omitempty"`
199
- UserID string `json:"user_id,omitempty"`
200
- AuthAt int64 `json:"auth_time,omitempty"`
201
- Email string `json:"email,omitempty"`
202
- EmailVerified bool `json:"email_verified"`
203
- Firebase *FirebaseClaim `json:"firebase,omitempty"`
181
+ Name string `json:"name,omitempty"`
182
+ Picture string `json:"picture,omitempty"`
183
+ UserID string `json:"user_id,omitempty"`
184
+ AuthAt *jwt.NumericDate `json:"auth_time,omitempty"`
185
+ Email string `json:"email,omitempty"`
186
+ EmailVerified bool `json:"email_verified"`
187
+ Firebase *FirebaseClaim `json:"firebase,omitempty"`
188
+
189
+ jwt.RegisteredClaims
190
+ }
191
+
192
+ func (c *Claims) validate(now time.Time, audience, issuer string) error {
193
+ if !c.VerifyExpiresAt(now, false) {
194
+ return jwt.ErrTokenExpired
195
+ } else if !c.VerifyIssuedAt(now, false) {
196
+ return jwt.ErrTokenUsedBeforeIssued
197
+ } else if !c.VerifyNotBefore(now, false) {
198
+ return jwt.ErrTokenNotValidYet
199
+ } else if !c.VerifyAudience(audience, true) {
200
+ return jwt.ErrTokenInvalidAudience
201
+ } else if !c.VerifyIssuer(issuer, true) {
202
+ return jwt.ErrTokenInvalidIssuer
203
+ } else if c.Subject == "" {
204
+ return errNoSubject
205
+ } else if c.AuthAt.After(now) {
206
+ return errAuthFuture
207
+ }
208
+ return nil
204
209
  }
205
210
 
206
211
  // FirebaseClaim represents firebase specific claim.
@@ -208,8 +213,3 @@ type FirebaseClaim struct {
208
213
  SignInProvider string `json:"sign_in_provider,omitempty"`
209
214
  Identities map[string][]string `json:"identities,omitempty"`
210
215
  }
211
-
212
- // Valid implements the jwt.Claims interface.
213
- func (c *Claims) Valid() error {
214
- return nil
215
- }
data/firejwt_test.go CHANGED
@@ -43,7 +43,7 @@ var _ = Describe("Validator", func() {
43
43
  certKID: string(certPEM),
44
44
  })
45
45
  }))
46
- seeds = mockClaims(time.Now().Unix())
46
+ seeds = mockClaims(time.Now())
47
47
 
48
48
  var err error
49
49
  subject, err = firejwt.Mocked(server.URL)
@@ -72,51 +72,51 @@ var _ = Describe("Validator", func() {
72
72
  })
73
73
 
74
74
  It("should verify exp", func() {
75
- seeds.ExpiresAt = time.Now().Unix() - 1
75
+ seeds.ExpiresAt = jwt.NewNumericDate(time.Now().Add(-time.Second))
76
76
  _, err := subject.Decode(generate())
77
- Expect(err).To(MatchError(`token has expired`))
77
+ Expect(err).To(MatchError(`token is expired`))
78
78
  Expect(err).To(BeAssignableToTypeOf(&jwt.ValidationError{}))
79
79
  })
80
80
 
81
81
  It("should verify iat", func() {
82
- seeds.IssuedAt = time.Now().Unix() + 1
82
+ seeds.IssuedAt = jwt.NewNumericDate(time.Now().Add(time.Second))
83
83
  _, err := subject.Decode(generate())
84
- Expect(err).To(MatchError(`issued in the future`))
84
+ Expect(err).To(MatchError(`token used before issued`))
85
85
  Expect(err).To(BeAssignableToTypeOf(&jwt.ValidationError{}))
86
86
  })
87
87
 
88
88
  It("should verify aud", func() {
89
- seeds.Audience = "other"
89
+ seeds.Audience = jwt.ClaimStrings{"other"}
90
90
  _, err := subject.Decode(generate())
91
- Expect(err).To(MatchError(`invalid audience claim "other"`))
91
+ Expect(err).To(MatchError(`token has invalid audience`))
92
92
  Expect(err).To(BeAssignableToTypeOf(&jwt.ValidationError{}))
93
93
  })
94
94
 
95
95
  It("should verify iss", func() {
96
96
  seeds.Issuer = "other"
97
97
  _, err := subject.Decode(generate())
98
- Expect(err).To(MatchError(`invalid issuer claim "other"`))
98
+ Expect(err).To(MatchError(`token has invalid issuer`))
99
99
  Expect(err).To(BeAssignableToTypeOf(&jwt.ValidationError{}))
100
100
  })
101
101
 
102
102
  It("should verify sub", func() {
103
103
  seeds.Subject = ""
104
104
  _, err := subject.Decode(generate())
105
- Expect(err).To(MatchError(`subject is missing`))
105
+ Expect(err).To(MatchError(`token has no subject`))
106
106
  Expect(err).To(BeAssignableToTypeOf(&jwt.ValidationError{}))
107
107
  })
108
108
 
109
109
  It("should verify auth time", func() {
110
- seeds.AuthAt = time.Now().Unix() + 1
110
+ seeds.AuthAt = jwt.NewNumericDate(time.Now().Add(time.Second))
111
111
  _, err := subject.Decode(generate())
112
- Expect(err).To(MatchError(`auth-time in the future`))
112
+ Expect(err).To(MatchError(`token auth_time not valid`))
113
113
  Expect(err).To(BeAssignableToTypeOf(&jwt.ValidationError{}))
114
114
  })
115
115
  })
116
116
 
117
117
  var _ = Describe("Claims", func() {
118
118
  It("should be JWT compatible", func() {
119
- subject := mockClaims(1515151515)
119
+ subject := mockClaims(time.Unix(1515151515, 0))
120
120
  Expect(json.Marshal(subject)).To(MatchJSON(`{
121
121
  "name": "Me",
122
122
  "picture": "https://test.host/me.jpg",
@@ -185,17 +185,12 @@ var _ = BeforeSuite(func() {
185
185
  certPEM = buf.String()
186
186
  })
187
187
 
188
- func mockClaims(now int64) *firejwt.Claims {
188
+ func mockClaims(now time.Time) *firejwt.Claims {
189
189
  return &firejwt.Claims{
190
190
  Name: "Me",
191
191
  Picture: "https://test.host/me.jpg",
192
- Subject: "MDYwNDQwNjUtYWQ0ZC00ZDkwLThl",
193
192
  UserID: "MDYwNDQwNjUtYWQ0ZC00ZDkwLThl",
194
- Audience: "mock-project",
195
- Issuer: "https://securetoken.google.com/mock-project",
196
- IssuedAt: now - 1800,
197
- ExpiresAt: now + 3600,
198
- AuthAt: now,
193
+ AuthAt: jwt.NewNumericDate(now),
199
194
  Email: "me@example.com",
200
195
  EmailVerified: true,
201
196
  Firebase: &firejwt.FirebaseClaim{
@@ -205,5 +200,12 @@ func mockClaims(now int64) *firejwt.Claims {
205
200
  "email": {"me@example.com"},
206
201
  },
207
202
  },
203
+ RegisteredClaims: jwt.RegisteredClaims{
204
+ Subject: "MDYwNDQwNjUtYWQ0ZC00ZDkwLThl",
205
+ Audience: jwt.ClaimStrings{"mock-project"},
206
+ Issuer: "https://securetoken.google.com/mock-project",
207
+ IssuedAt: jwt.NewNumericDate(now.Add(-30 * time.Minute)),
208
+ ExpiresAt: jwt.NewNumericDate(now.Add(time.Hour)),
209
+ },
208
210
  }
209
211
  }
data/go.mod CHANGED
@@ -5,5 +5,5 @@ go 1.16
5
5
  require (
6
6
  github.com/bsm/ginkgo/v2 v2.0.0
7
7
  github.com/bsm/gomega v1.11.0
8
- github.com/golang-jwt/jwt/v4 v4.3.0
8
+ github.com/golang-jwt/jwt/v4 v4.4.0
9
9
  )
data/go.sum CHANGED
@@ -2,5 +2,5 @@ github.com/bsm/ginkgo/v2 v2.0.0 h1:JZAs5u7SmJCrZlGFrC3v1stg/uC7OIj2q2FW9NxeIVE=
2
2
  github.com/bsm/ginkgo/v2 v2.0.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
3
3
  github.com/bsm/gomega v1.11.0 h1:wg9DVGPETNZLIbMsseneMV1a7uo/x+wsCyNXdEcifDI=
4
4
  github.com/bsm/gomega v1.11.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk=
5
- github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
6
- github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
5
+ github.com/golang-jwt/jwt/v4 v4.4.0 h1:EmVIxB5jzbllGIjiCV5JG4VylbK3KE400tLGLI1cdfU=
6
+ github.com/golang-jwt/jwt/v4 v4.4.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: firejwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Black Square Media Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-18 00:00:00.000000000 Z
11
+ date: 2022-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -121,7 +121,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: '2.6'
124
+ version: '2.7'
125
125
  required_rubygems_version: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - ">="