firejwt 0.3.2 → 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: e2ba30384baf7a421ad475e635a354097b6d3f7fa9fbb19551d8d8686bc06c5a
4
- data.tar.gz: ef29232ef99cfd4076e2b99826c350c62b06332d21eca83fd72ed60069b6413e
3
+ metadata.gz: ab69a025ab89bc78cc7100320e86215b969e65b861321e8166ff0facf2aca0c7
4
+ data.tar.gz: 566c9ec8876efdde35f727a61c2b6ffc87203c241bd287ee5dbea302f35a5d5b
5
5
  SHA512:
6
- metadata.gz: c20c9c61e6e70edfddc428c155f2f14c1810cf3460cba4f90735d7aa09be68c44528ba737ae6038748259dcbc3763366256445e96c9047fe91271babff593196
7
- data.tar.gz: 4415e3995f78726cd1ab25920a83dc3468daadc111bd91dec87513374c8013b56d2cce074e66a6f2ab2fdd1856193406b29434e1aa13dc8aa802aba3b7e81637
6
+ metadata.gz: 254b2653d48d41c64b4837e044a19f2412ffd93a019417c1ddf050b715643c0502b93a11439cefde9e13f9ab7416eb5b059f57a4ca9b8b080ad4ad4e7780be74
7
+ data.tar.gz: 44eb4c65193a62c0a3ae38d2c80898c623168f267caa74080260a9c37c59148a9918ec9e02371e6bb03358705873d5b8314f993cc2707692e5938a4de559f6bd
@@ -7,11 +7,20 @@ on:
7
7
  branches:
8
8
  - main
9
9
  jobs:
10
+ golint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v2
15
+ - name: Run lint
16
+ uses: golangci/golangci-lint-action@v2
17
+ with:
18
+ version: latest
10
19
  go:
11
20
  runs-on: ubuntu-latest
12
21
  strategy:
13
22
  matrix:
14
- go-version: [1.15.x, 1.16.x]
23
+ go-version: [1.18.x, 1.17.x]
15
24
  steps:
16
25
  - name: Checkout
17
26
  uses: actions/checkout@v2
@@ -32,7 +41,7 @@ jobs:
32
41
  runs-on: ubuntu-latest
33
42
  strategy:
34
43
  matrix:
35
- ruby-version: ["2.5", "2.6", "2.7", "3.0"]
44
+ ruby-version: ["2.7", "3.0", "3.1"]
36
45
  steps:
37
46
  - uses: actions/checkout@v2
38
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.5"
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.3.2)
4
+ firejwt (0.5.0)
5
5
  jwt
6
6
 
7
7
  GEM
@@ -12,58 +12,57 @@ GEM
12
12
  ast (2.4.2)
13
13
  crack (0.4.5)
14
14
  rexml
15
- diff-lcs (1.4.4)
15
+ diff-lcs (1.5.0)
16
16
  hashdiff (1.0.1)
17
- jwt (2.2.3)
18
- parallel (1.20.1)
19
- parser (3.0.2.0)
17
+ jwt (2.3.0)
18
+ parallel (1.21.0)
19
+ parser (3.1.1.0)
20
20
  ast (~> 2.4.1)
21
21
  public_suffix (4.0.6)
22
- rainbow (3.0.0)
22
+ rainbow (3.1.1)
23
23
  rake (13.0.6)
24
- regexp_parser (2.1.1)
24
+ regexp_parser (2.2.1)
25
25
  rexml (3.2.5)
26
- rspec (3.10.0)
27
- rspec-core (~> 3.10.0)
28
- rspec-expectations (~> 3.10.0)
29
- rspec-mocks (~> 3.10.0)
30
- rspec-core (3.10.1)
31
- rspec-support (~> 3.10.0)
32
- rspec-expectations (3.10.1)
26
+ rspec (3.11.0)
27
+ rspec-core (~> 3.11.0)
28
+ rspec-expectations (~> 3.11.0)
29
+ rspec-mocks (~> 3.11.0)
30
+ rspec-core (3.11.0)
31
+ rspec-support (~> 3.11.0)
32
+ rspec-expectations (3.11.0)
33
33
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.10.0)
35
- rspec-mocks (3.10.2)
34
+ rspec-support (~> 3.11.0)
35
+ rspec-mocks (3.11.0)
36
36
  diff-lcs (>= 1.2.0, < 2.0)
37
- rspec-support (~> 3.10.0)
38
- rspec-support (3.10.2)
39
- rubocop (1.18.4)
37
+ rspec-support (~> 3.11.0)
38
+ rspec-support (3.11.0)
39
+ rubocop (1.26.0)
40
40
  parallel (~> 1.10)
41
- parser (>= 3.0.0.0)
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.8.0, < 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.8.0)
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.11.4)
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.4.0)
61
- rubocop (~> 1.0)
62
- rubocop-ast (>= 1.1.0)
60
+ rubocop-rspec (2.9.0)
61
+ rubocop (~> 1.19)
63
62
  ruby-progressbar (1.11.0)
64
- unicode-display_width (2.0.0)
65
- webmock (3.13.0)
66
- addressable (>= 2.3.6)
63
+ unicode-display_width (2.1.0)
64
+ webmock (3.14.0)
65
+ addressable (>= 2.8.0)
67
66
  crack (>= 0.3.2)
68
67
  hashdiff (>= 0.4.0, < 2.0.0)
69
68
 
@@ -78,4 +77,4 @@ DEPENDENCIES
78
77
  webmock
79
78
 
80
79
  BUNDLED WITH
81
- 2.2.21
80
+ 2.3.6
data/firejwt.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'firejwt'
3
- s.version = '0.3.2'
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,11 +11,14 @@ 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.5'
14
+ s.required_ruby_version = '>= 2.7'
15
15
 
16
16
  s.add_dependency 'jwt'
17
17
  s.add_development_dependency 'rake'
18
18
  s.add_development_dependency 'rspec'
19
19
  s.add_development_dependency 'rubocop-bsm'
20
20
  s.add_development_dependency 'webmock'
21
+ s.metadata = {
22
+ 'rubygems_mfa_required' => 'true',
23
+ }
21
24
  end
data/firejwt.go CHANGED
@@ -13,9 +13,13 @@ import (
13
13
  "sync/atomic"
14
14
  "time"
15
15
 
16
- "github.com/golang-jwt/jwt"
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
@@ -17,9 +17,9 @@ import (
17
17
  "time"
18
18
 
19
19
  "github.com/bsm/firejwt"
20
- . "github.com/bsm/ginkgo"
20
+ . "github.com/bsm/ginkgo/v2"
21
21
  . "github.com/bsm/gomega"
22
- "github.com/golang-jwt/jwt"
22
+ "github.com/golang-jwt/jwt/v4"
23
23
  )
24
24
 
25
25
  var _ = Describe("Validator", func() {
@@ -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
@@ -1,9 +1,9 @@
1
1
  module github.com/bsm/firejwt
2
2
 
3
- go 1.13
3
+ go 1.16
4
4
 
5
5
  require (
6
- github.com/bsm/ginkgo v1.16.1
6
+ github.com/bsm/ginkgo/v2 v2.0.0
7
7
  github.com/bsm/gomega v1.11.0
8
- github.com/golang-jwt/jwt v3.2.1+incompatible
8
+ github.com/golang-jwt/jwt/v4 v4.4.0
9
9
  )
data/go.sum CHANGED
@@ -1,6 +1,6 @@
1
- github.com/bsm/ginkgo v1.16.1 h1:jp1v1dbmbGZDWmnGXDTN+XK3U1fTTNja9xYa7VBI0l0=
2
- github.com/bsm/ginkgo v1.16.1/go.mod h1:RabIZLzOCPghgHJKUqHZpqrQETA5AnF4aCSIYy5C1bk=
1
+ github.com/bsm/ginkgo/v2 v2.0.0 h1:JZAs5u7SmJCrZlGFrC3v1stg/uC7OIj2q2FW9NxeIVE=
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 v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
6
- github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
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.3.2
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: 2021-07-27 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
@@ -87,7 +87,6 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
- - ".github/workflows/lint.yml"
91
90
  - ".github/workflows/test.yml"
92
91
  - ".gitignore"
93
92
  - ".rubocop.yml"
@@ -112,7 +111,8 @@ files:
112
111
  homepage: https://github.com/bsm/firejwt
113
112
  licenses:
114
113
  - Apache-2.0
115
- metadata: {}
114
+ metadata:
115
+ rubygems_mfa_required: 'true'
116
116
  post_install_message:
117
117
  rdoc_options: []
118
118
  require_paths:
@@ -121,14 +121,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: '2.5'
124
+ version: '2.7'
125
125
  required_rubygems_version: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - ">="
128
128
  - !ruby/object:Gem::Version
129
129
  version: '0'
130
130
  requirements: []
131
- rubygems_version: 3.2.15
131
+ rubygems_version: 3.3.3
132
132
  signing_key:
133
133
  specification_version: 4
134
134
  summary: Firebase JWT validation
@@ -1,18 +0,0 @@
1
- name: Lint
2
- on:
3
- push:
4
- branches:
5
- - main
6
- pull_request:
7
- branches:
8
- - main
9
- jobs:
10
- golangci:
11
- runs-on: ubuntu-latest
12
- steps:
13
- - name: Checkout
14
- uses: actions/checkout@v2
15
- - name: Run lint
16
- uses: golangci/golangci-lint-action@v2
17
- with:
18
- version: latest