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 +4 -4
- data/.github/workflows/test.yml +2 -2
- data/.rubocop.yml +1 -1
- data/Gemfile.lock +8 -8
- data/firejwt.gemspec +2 -2
- data/firejwt.go +37 -37
- data/firejwt_test.go +21 -19
- data/go.mod +1 -1
- data/go.sum +2 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab69a025ab89bc78cc7100320e86215b969e65b861321e8166ff0facf2aca0c7
|
4
|
+
data.tar.gz: 566c9ec8876efdde35f727a61c2b6ffc87203c241bd287ee5dbea302f35a5d5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 254b2653d48d41c64b4837e044a19f2412ffd93a019417c1ddf050b715643c0502b93a11439cefde9e13f9ab7416eb5b059f57a4ca9b8b080ad4ad4e7780be74
|
7
|
+
data.tar.gz: 44eb4c65193a62c0a3ae38d2c80898c623168f267caa74080260a9c37c59148a9918ec9e02371e6bb03358705873d5b8314f993cc2707692e5938a4de559f6bd
|
data/.github/workflows/test.yml
CHANGED
@@ -20,7 +20,7 @@ jobs:
|
|
20
20
|
runs-on: ubuntu-latest
|
21
21
|
strategy:
|
22
22
|
matrix:
|
23
|
-
go-version: [1.
|
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.
|
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
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
firejwt (0.
|
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.
|
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.
|
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.
|
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.
|
49
|
-
parser (>= 3.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
104
|
-
|
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.
|
124
|
-
return nil,
|
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
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
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()
|
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().
|
75
|
+
seeds.ExpiresAt = jwt.NewNumericDate(time.Now().Add(-time.Second))
|
76
76
|
_, err := subject.Decode(generate())
|
77
|
-
Expect(err).To(MatchError(`token
|
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().
|
82
|
+
seeds.IssuedAt = jwt.NewNumericDate(time.Now().Add(time.Second))
|
83
83
|
_, err := subject.Decode(generate())
|
84
|
-
Expect(err).To(MatchError(`
|
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
|
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
|
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(`
|
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().
|
110
|
+
seeds.AuthAt = jwt.NewNumericDate(time.Now().Add(time.Second))
|
111
111
|
_, err := subject.Decode(generate())
|
112
|
-
Expect(err).To(MatchError(`
|
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
|
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
|
-
|
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
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.
|
6
|
-
github.com/golang-jwt/jwt/v4 v4.
|
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
|
+
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-
|
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.
|
124
|
+
version: '2.7'
|
125
125
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
126
|
requirements:
|
127
127
|
- - ">="
|