firejwt 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  - - ">="