strongmind-auth 1.1.142 → 1.2.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 +4 -4
- data/app/controllers/concerns/jwt_utilities.rb +72 -1
- data/lib/strongmind/auth/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1c87c87745c0a96b93591482bfb073b7aee75ad305af4acc7d32632e52f2e51
|
|
4
|
+
data.tar.gz: 5f199cf554635e03c7949ab35ae0e8740ccfef983c1d92fe5d1c72b839367d50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c71f32bab0c99a7c87d0b62121d26f61ad8c67841ad67ac80968d2db28cbf1c14e089ea2afc147aaad36a8e2c6040d40fa206db831e9f8f453468d58374a3ef1
|
|
7
|
+
data.tar.gz: 9aeae95af157a89828bd820983c86b4452b6ad0b48f4096836d69d24d41a533d943a7f74d3eeceec4c209a1155539d9cff0579e18c892feccd59c0d377381a9f
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
module JwtUtilities
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
6
|
|
|
7
|
+
# Matches Zitadel's role-grant claim, e.g. "urn:zitadel:iam:org:project:123:roles"
|
|
8
|
+
# or the project-agnostic "urn:zitadel:iam:org:project:roles".
|
|
9
|
+
ZITADEL_ROLES_CLAIM_PATTERN = /\Aurn:zitadel:iam:org:project:(?:[^:]+:)?roles\z/.freeze
|
|
10
|
+
|
|
7
11
|
def jwt_valid?(jwt, condition_key = nil, scopes = [], attributes = [])
|
|
8
12
|
payload = decode_jwt(jwt)
|
|
9
13
|
return false unless payload
|
|
@@ -41,6 +45,10 @@ module JwtUtilities
|
|
|
41
45
|
verify_iat: true,
|
|
42
46
|
verify_iss: true,
|
|
43
47
|
verify_sub: true,
|
|
48
|
+
# RS256 matches Zitadel's current default signing algorithm.
|
|
49
|
+
# While Zitadel supports EdDSA (Ed25519), the platform is not currently
|
|
50
|
+
# configured to use it. When/if that changes, migrate this to EdDSA for
|
|
51
|
+
# post-quantum resistance. See also: go/auth/middleware.go (strongmind-platform).
|
|
44
52
|
algorithm: 'RS256',
|
|
45
53
|
iss: ENV['IDENTITY_BASE_URL'],
|
|
46
54
|
leeway: 60
|
|
@@ -48,7 +56,70 @@ module JwtUtilities
|
|
|
48
56
|
end
|
|
49
57
|
|
|
50
58
|
def scope_valid?(payload, scopes)
|
|
51
|
-
|
|
59
|
+
return true if scopes.empty?
|
|
60
|
+
|
|
61
|
+
legacy_scope_valid?(payload, scopes) || roles_valid?(payload, scopes)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def legacy_scope_valid?(payload, scopes)
|
|
65
|
+
payload['scope'].present? && scopes.all? { |scope| payload['scope'].include?(scope) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Zitadel roles are provisioned 1-to-1 with the legacy scope strings, so a
|
|
69
|
+
# required scope is satisfied by a same-named grant in the roles claim.
|
|
70
|
+
# Comparison is case-insensitive to handle different casing conventions.
|
|
71
|
+
def roles_valid?(payload, scopes)
|
|
72
|
+
project_id = ENV['IDENTITY_PROJECT_ID']
|
|
73
|
+
return false if project_id.blank?
|
|
74
|
+
return false unless audience_valid?(payload)
|
|
75
|
+
return false unless project_id_valid?(payload)
|
|
76
|
+
|
|
77
|
+
roles = zitadel_roles(payload, project_id)
|
|
78
|
+
return false unless roles.present?
|
|
79
|
+
|
|
80
|
+
normalized_roles = roles.keys.map(&:downcase).to_set
|
|
81
|
+
scopes.all? { |scope| normalized_roles.include?(scope.downcase) }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def zitadel_roles(payload, project_id = nil)
|
|
85
|
+
payload.keys
|
|
86
|
+
.select { |key| key.to_s.match?(ZITADEL_ROLES_CLAIM_PATTERN) }
|
|
87
|
+
.select do |key|
|
|
88
|
+
claim_project_id = extract_project_id(key)
|
|
89
|
+
project_id.blank? || claim_project_id.nil? || claim_project_id == project_id
|
|
90
|
+
end
|
|
91
|
+
.each_with_object({}) do |key, acc|
|
|
92
|
+
value = payload[key]
|
|
93
|
+
acc.merge!(value) if value.is_a?(Hash)
|
|
94
|
+
end
|
|
95
|
+
.presence
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def audience_valid?(payload)
|
|
99
|
+
aud_list = Array(payload['aud'])
|
|
100
|
+
|
|
101
|
+
if ENV['IDENTITY_PROJECT_ID'].present?
|
|
102
|
+
aud_list.include?(ENV['IDENTITY_PROJECT_ID'])
|
|
103
|
+
elsif ENV['IDENTITY_CLIENT_ID'].present?
|
|
104
|
+
aud_list.include?(ENV['IDENTITY_CLIENT_ID'])
|
|
105
|
+
else
|
|
106
|
+
true
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def project_id_valid?(payload)
|
|
111
|
+
roles_claims = payload.keys.select { |key| key.to_s.match?(ZITADEL_ROLES_CLAIM_PATTERN) }
|
|
112
|
+
return true if roles_claims.empty?
|
|
113
|
+
return true if ENV['IDENTITY_PROJECT_ID'].blank?
|
|
114
|
+
|
|
115
|
+
roles_claims.any? do |claim_key|
|
|
116
|
+
project_id = extract_project_id(claim_key)
|
|
117
|
+
project_id.nil? || project_id == ENV['IDENTITY_PROJECT_ID']
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def extract_project_id(claim_key)
|
|
122
|
+
claim_key.to_s[/project:([^:]+):/, 1]
|
|
52
123
|
end
|
|
53
124
|
|
|
54
125
|
def nonce_valid?(payload)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: strongmind-auth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Team Belding
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|