omniauth_strong_auth_oidc 0.1.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 +7 -0
- data/.gitignore +34 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +6 -0
- data/CONTRIBUTING.md +9 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +313 -0
- data/Rakefile +12 -0
- data/lib/generators/omniauth_strong_auth_oidc/install_generator.rb +73 -0
- data/lib/generators/omniauth_strong_auth_oidc/templates/relying_party_entity_statement_controller.rb.tt +61 -0
- data/lib/omniauth/strategies/strong_auth_oidc.rb +210 -0
- data/lib/omniauth_strong_auth_oidc/entity_statement.rb +22 -0
- data/lib/omniauth_strong_auth_oidc/entity_statement_fetcher/base.rb +37 -0
- data/lib/omniauth_strong_auth_oidc/entity_statement_fetcher/federation_url_fetcher.rb +29 -0
- data/lib/omniauth_strong_auth_oidc/entity_statement_fetcher/file_fetcher.rb +22 -0
- data/lib/omniauth_strong_auth_oidc/entity_statement_fetcher.rb +9 -0
- data/lib/omniauth_strong_auth_oidc/jwks_cache.rb +31 -0
- data/lib/omniauth_strong_auth_oidc/jwks_fetcher.rb +87 -0
- data/lib/omniauth_strong_auth_oidc/relying_party_entity_statement_generator.rb +77 -0
- data/lib/omniauth_strong_auth_oidc/relying_party_jwks_generator.rb +50 -0
- data/lib/omniauth_strong_auth_oidc/relying_party_jwks_storage/base.rb +101 -0
- data/lib/omniauth_strong_auth_oidc/relying_party_jwks_storage/cache_storage.rb +235 -0
- data/lib/omniauth_strong_auth_oidc/relying_party_jwks_storage/env_storage.rb +112 -0
- data/lib/omniauth_strong_auth_oidc/relying_party_jwks_storage.rb +10 -0
- data/lib/omniauth_strong_auth_oidc/version.rb +3 -0
- data/lib/omniauth_strong_auth_oidc.rb +15 -0
- data/omniauth_strong_auth_oidc.gemspec +32 -0
- metadata +494 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
module OmniauthStrongAuthOidc
|
|
2
|
+
module RelyingPartyJwksStorage
|
|
3
|
+
# Cached implementation of KeyStorage that stores keys in Rails cache
|
|
4
|
+
# Returns a single JWT::JWK::Set containing both signing and encryption keys
|
|
5
|
+
# Supports key rotation while keeping previous keys available
|
|
6
|
+
class CacheStorage < Base
|
|
7
|
+
CACHE_PREFIX = 'telia_oidc:keys'
|
|
8
|
+
CURRENT_SIGNING_KEY = "#{CACHE_PREFIX}:signing:current"
|
|
9
|
+
PREVIOUS_SIGNING_KEYS = "#{CACHE_PREFIX}:signing:previous"
|
|
10
|
+
CURRENT_ENCRYPTION_KEY = "#{CACHE_PREFIX}:encryption:current"
|
|
11
|
+
PREVIOUS_ENCRYPTION_KEYS = "#{CACHE_PREFIX}:encryption:previous"
|
|
12
|
+
MAX_PREVIOUS_KEYS = 2 # Keep up to 2 previous keys for rotation
|
|
13
|
+
|
|
14
|
+
attr_reader :cache_store
|
|
15
|
+
|
|
16
|
+
# @param key_length [Integer] RSA key length in bits (default: 4096)
|
|
17
|
+
# @param cache_store [ActiveSupport::Cache::Store] Cache store to use (Rails.cache for example)
|
|
18
|
+
def initialize(key_length: DEFAULT_KEY_LENGTH, cache_store:)
|
|
19
|
+
@key_length = key_length
|
|
20
|
+
@cache_store = cache_store
|
|
21
|
+
validate_key_length!
|
|
22
|
+
ensure_keys_exist!
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def support_rotation?
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns the JWK set containing all keys (signing + encryption, current + previous)
|
|
30
|
+
# @return [JWT::JWK::Set]
|
|
31
|
+
def jwks
|
|
32
|
+
@jwks ||= create_jwk_set(
|
|
33
|
+
signing_key_data: all_signing_keys,
|
|
34
|
+
encryption_key_data: all_encryption_keys
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Rotates the signing key, moving current to previous
|
|
39
|
+
# @return [JWT::JWK] The new signing JWK
|
|
40
|
+
def rotate_signing_key!
|
|
41
|
+
old_key_data = load_key_data(CURRENT_SIGNING_KEY)
|
|
42
|
+
new_key = generate_rsa_key(@key_length)
|
|
43
|
+
new_key_data = {
|
|
44
|
+
pem: new_key.to_pem,
|
|
45
|
+
created_at: Time.now.to_i
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Move current key to previous keys list
|
|
49
|
+
add_to_previous_keys(PREVIOUS_SIGNING_KEYS, old_key_data)
|
|
50
|
+
|
|
51
|
+
# Store new key as current
|
|
52
|
+
store_key_data(CURRENT_SIGNING_KEY, new_key_data)
|
|
53
|
+
|
|
54
|
+
# Clear cached JWK set
|
|
55
|
+
clear_jwks_cache!
|
|
56
|
+
|
|
57
|
+
JWT::JWK.new(new_key, use: 'sig') # Return JWK representation of
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Rotates the encryption key, moving current to previous
|
|
61
|
+
# @return [JWT::JWK] The new encryption JWK
|
|
62
|
+
def rotate_encryption_key!
|
|
63
|
+
old_key_data = load_key_data(CURRENT_ENCRYPTION_KEY)
|
|
64
|
+
new_key = generate_rsa_key(@key_length)
|
|
65
|
+
new_key_data = {
|
|
66
|
+
pem: new_key.to_pem,
|
|
67
|
+
created_at: Time.now.to_i
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Move current key to previous keys list
|
|
71
|
+
add_to_previous_keys(PREVIOUS_ENCRYPTION_KEYS, old_key_data)
|
|
72
|
+
|
|
73
|
+
# Store new key as current
|
|
74
|
+
store_key_data(CURRENT_ENCRYPTION_KEY, new_key_data)
|
|
75
|
+
|
|
76
|
+
# Clear cached JWK set
|
|
77
|
+
clear_jwks_cache!
|
|
78
|
+
|
|
79
|
+
JWT::JWK.new(new_key, use: 'enc') # Return JWK representation of
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Clear cached JWK set (useful for testing or after rotation)
|
|
83
|
+
# @return [void]
|
|
84
|
+
def clear_jwks_cache!
|
|
85
|
+
@jwks = nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Completely removes all keys from storage (use with caution!)
|
|
89
|
+
# @return [void]
|
|
90
|
+
def self.clear_all_keys!(cache_store)
|
|
91
|
+
store = cache_store
|
|
92
|
+
store.delete(CURRENT_SIGNING_KEY)
|
|
93
|
+
store.delete(PREVIOUS_SIGNING_KEYS)
|
|
94
|
+
store.delete(CURRENT_ENCRYPTION_KEY)
|
|
95
|
+
store.delete(PREVIOUS_ENCRYPTION_KEYS)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
# Returns the current signing key (for internal use)
|
|
101
|
+
# @return [OpenSSL::PKey::RSA]
|
|
102
|
+
def current_signing_key
|
|
103
|
+
load_key(CURRENT_SIGNING_KEY)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Returns the current encryption key (for internal use)
|
|
107
|
+
# @return [OpenSSL::PKey::RSA]
|
|
108
|
+
def current_encryption_key
|
|
109
|
+
load_key(CURRENT_ENCRYPTION_KEY)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Returns all signing keys (current + previous) with their metadata
|
|
113
|
+
# @return [Array<OpenSSL::PKey::RSA>]
|
|
114
|
+
def all_signing_keys
|
|
115
|
+
current_data = load_key_data(CURRENT_SIGNING_KEY)
|
|
116
|
+
previous_data = load_previous_key_data(PREVIOUS_SIGNING_KEYS)
|
|
117
|
+
|
|
118
|
+
[current_data] + previous_data
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Returns all encryption keys (current + previous) with their metadata
|
|
122
|
+
# @return [Array<OpenSSL::PKey::RSA>]
|
|
123
|
+
def all_encryption_keys
|
|
124
|
+
current_data = load_key_data(CURRENT_ENCRYPTION_KEY)
|
|
125
|
+
previous_data = load_previous_key_data(PREVIOUS_ENCRYPTION_KEYS)
|
|
126
|
+
|
|
127
|
+
[current_data] + previous_data
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def validate_key_length!
|
|
131
|
+
raise ArgumentError, "Key length must be at least #{MINIMUM_KEY_LENGTH} bits" if @key_length < MINIMUM_KEY_LENGTH
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def ensure_keys_exist!
|
|
135
|
+
# Generate signing key if it doesn't exist
|
|
136
|
+
unless cache_store.exist?(CURRENT_SIGNING_KEY)
|
|
137
|
+
key_data = {
|
|
138
|
+
pem: generate_rsa_key(@key_length).to_pem,
|
|
139
|
+
created_at: Time.now.to_i
|
|
140
|
+
}
|
|
141
|
+
store_key_data(CURRENT_SIGNING_KEY, key_data)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Generate encryption key if it doesn't exist
|
|
145
|
+
unless cache_store.exist?(CURRENT_ENCRYPTION_KEY)
|
|
146
|
+
key_data = {
|
|
147
|
+
pem: generate_rsa_key(@key_length).to_pem,
|
|
148
|
+
created_at: Time.now.to_i
|
|
149
|
+
}
|
|
150
|
+
store_key_data(CURRENT_ENCRYPTION_KEY, key_data)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Initialize previous keys arrays if they don't exist
|
|
154
|
+
cache_store.write(PREVIOUS_SIGNING_KEYS, []) unless cache_store.exist?(PREVIOUS_SIGNING_KEYS)
|
|
155
|
+
cache_store.write(PREVIOUS_ENCRYPTION_KEYS, []) unless cache_store.exist?(PREVIOUS_ENCRYPTION_KEYS)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def load_key(cache_key)
|
|
159
|
+
key_data = cache_store.read(cache_key)
|
|
160
|
+
raise "Key not found in cache: #{cache_key}" unless key_data
|
|
161
|
+
|
|
162
|
+
# Handle legacy PEM-only format
|
|
163
|
+
if key_data.is_a?(String)
|
|
164
|
+
return OpenSSL::PKey::RSA.new(key_data)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
OpenSSL::PKey::RSA.new(key_data[:pem])
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def load_key_data(cache_key)
|
|
171
|
+
key_data = cache_store.read(cache_key)
|
|
172
|
+
raise "Key not found in cache: #{cache_key}" unless key_data
|
|
173
|
+
|
|
174
|
+
# Handle legacy PEM-only format
|
|
175
|
+
if key_data.is_a?(String)
|
|
176
|
+
return { pem: key_data, kid: nil, created_at: nil }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
key_data
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def store_key(cache_key, key)
|
|
183
|
+
# This method is for backward compatibility
|
|
184
|
+
pem_data = key.to_pem
|
|
185
|
+
cache_store.write(cache_key, pem_data)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def store_key_data(cache_key, key_data)
|
|
189
|
+
cache_store.write(cache_key, key_data)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def previous_signing_keys
|
|
193
|
+
load_previous_keys(PREVIOUS_SIGNING_KEYS)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def previous_encryption_keys
|
|
197
|
+
load_previous_keys(PREVIOUS_ENCRYPTION_KEYS)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def load_previous_keys(cache_key)
|
|
201
|
+
key_data_list = cache_store.read(cache_key) || []
|
|
202
|
+
key_data_list.map do |data|
|
|
203
|
+
# Handle legacy PEM-only format
|
|
204
|
+
if data.is_a?(String)
|
|
205
|
+
OpenSSL::PKey::RSA.new(data)
|
|
206
|
+
else
|
|
207
|
+
OpenSSL::PKey::RSA.new(data[:pem])
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def load_previous_key_data(cache_key)
|
|
213
|
+
key_data_list = cache_store.read(cache_key) || []
|
|
214
|
+
key_data_list.map do |data|
|
|
215
|
+
# Handle legacy PEM-only format
|
|
216
|
+
if data.is_a?(String)
|
|
217
|
+
{ pem: data, kid: nil, created_at: nil }
|
|
218
|
+
else
|
|
219
|
+
data
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def add_to_previous_keys(cache_key, key_data)
|
|
225
|
+
previous_keys = cache_store.read(cache_key) || []
|
|
226
|
+
previous_keys.unshift(key_data)
|
|
227
|
+
|
|
228
|
+
# Keep only the most recent keys
|
|
229
|
+
previous_keys = previous_keys.first(MAX_PREVIOUS_KEYS)
|
|
230
|
+
|
|
231
|
+
cache_store.write(cache_key, previous_keys)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
module OmniauthStrongAuthOidc
|
|
2
|
+
module RelyingPartyJwksStorage
|
|
3
|
+
# Environment variable-based key storage for production deployments
|
|
4
|
+
# Loads RSA keys from environment variables without rotation support
|
|
5
|
+
# Keys should be base64-encoded PEM-encoded RSA private keys
|
|
6
|
+
#
|
|
7
|
+
# Default environment variables:
|
|
8
|
+
# - OIDC_SIGNING_KEY_BASE64: Base64-encoded PEM RSA private key for signing
|
|
9
|
+
# - OIDC_ENCRYPTION_KEY_BASE64: Base64-encoded PEM RSA private key for encryption
|
|
10
|
+
#
|
|
11
|
+
# Example with custom environment variable names:
|
|
12
|
+
# RelyingPartyJwksStorage::EnvStorage.new(
|
|
13
|
+
# signing_key_env: 'MY_SIGNING_KEY',
|
|
14
|
+
# encryption_key_env: 'MY_ENCRYPTION_KEY'
|
|
15
|
+
# )
|
|
16
|
+
#
|
|
17
|
+
# To create a storage with only signing key (no encryption):
|
|
18
|
+
# RelyingPartyJwksStorage::EnvStorage.new(
|
|
19
|
+
# signing_key_env: 'ENTITY_STATEMENT_SIGNING_KEY',
|
|
20
|
+
# encryption_key_env: nil
|
|
21
|
+
# )
|
|
22
|
+
class EnvStorage < Base
|
|
23
|
+
DEFAULT_SIGNING_KEY_ENV = 'OIDC_SIGNING_KEY_BASE64'
|
|
24
|
+
DEFAULT_ENCRYPTION_KEY_ENV = 'OIDC_ENCRYPTION_KEY_BASE64'
|
|
25
|
+
|
|
26
|
+
attr_reader :signing_key_env, :encryption_key_env
|
|
27
|
+
|
|
28
|
+
def initialize(signing_key_env: DEFAULT_SIGNING_KEY_ENV, encryption_key_env: DEFAULT_ENCRYPTION_KEY_ENV)
|
|
29
|
+
@signing_key_env = signing_key_env
|
|
30
|
+
@encryption_key_env = encryption_key_env
|
|
31
|
+
validate_env_keys!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def support_rotation?
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns the JWK set containing both signing and encryption keys
|
|
39
|
+
# @return [JWT::JWK::Set]
|
|
40
|
+
def jwks
|
|
41
|
+
@jwks ||= begin
|
|
42
|
+
encryption_data = encryption_key_env && !ENV[encryption_key_env].nil? && !ENV[encryption_key_env].empty? ? [{ pem: encryption_key_pem }] : []
|
|
43
|
+
|
|
44
|
+
create_jwk_set(
|
|
45
|
+
signing_key_data: [{ pem: signing_key_pem }],
|
|
46
|
+
encryption_key_data: encryption_data
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Rotation is not supported for environment-based keys
|
|
52
|
+
# @raise [NotImplementedError]
|
|
53
|
+
def rotate_signing_key!
|
|
54
|
+
raise NotImplementedError, "Key rotation is not supported for EnvKeyStorage. Update environment variables instead."
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Rotation is not supported for environment-based keys
|
|
58
|
+
# @raise [NotImplementedError]
|
|
59
|
+
def rotate_encryption_key!
|
|
60
|
+
raise NotImplementedError, "Key rotation is not supported for EnvKeyStorage. Update environment variables instead."
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def signing_key_pem
|
|
66
|
+
Base64.decode64(ENV[signing_key_env])
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def encryption_key_pem
|
|
70
|
+
return nil unless encryption_key_env && !ENV[encryption_key_env].nil? && !ENV[encryption_key_env].empty?
|
|
71
|
+
Base64.decode64(ENV[encryption_key_env])
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def validate_env_keys!
|
|
75
|
+
# Signing key is always required
|
|
76
|
+
if ENV[signing_key_env].nil? || ENV[signing_key_env].empty?
|
|
77
|
+
raise ArgumentError, "Missing required environment variable: #{signing_key_env}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Validate signing key
|
|
81
|
+
begin
|
|
82
|
+
OpenSSL::PKey::RSA.new(signing_key_pem)
|
|
83
|
+
rescue OpenSSL::PKey::RSAError => e
|
|
84
|
+
raise ArgumentError, "Invalid RSA key in #{signing_key_env}: #{e.message}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
signing_key = OpenSSL::PKey::RSA.new(signing_key_pem)
|
|
88
|
+
if signing_key.n.num_bits < MINIMUM_KEY_LENGTH
|
|
89
|
+
raise ArgumentError, "#{signing_key_env} must be at least #{MINIMUM_KEY_LENGTH} bits (got #{signing_key.n.num_bits} bits)"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Return early if no encryption key is configured
|
|
93
|
+
return unless encryption_key_env
|
|
94
|
+
|
|
95
|
+
if ENV[encryption_key_env].nil? || ENV[encryption_key_env].empty?
|
|
96
|
+
raise ArgumentError, "Missing required environment variable: #{encryption_key_env}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
begin
|
|
100
|
+
OpenSSL::PKey::RSA.new(encryption_key_pem)
|
|
101
|
+
rescue OpenSSL::PKey::RSAError => e
|
|
102
|
+
raise ArgumentError, "Invalid RSA key in #{encryption_key_env}: #{e.message}"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
encryption_key = OpenSSL::PKey::RSA.new(encryption_key_pem)
|
|
106
|
+
if encryption_key.n.num_bits < MINIMUM_KEY_LENGTH
|
|
107
|
+
raise ArgumentError, "#{encryption_key_env} must be at least #{MINIMUM_KEY_LENGTH} bits (got #{encryption_key.n.num_bits} bits)"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require_relative 'relying_party_jwks_storage/base'
|
|
2
|
+
require_relative 'relying_party_jwks_storage/cache_storage'
|
|
3
|
+
require_relative 'relying_party_jwks_storage/env_storage'
|
|
4
|
+
module OmniauthStrongAuthOidc
|
|
5
|
+
# Abstract interface for OpenID key storage
|
|
6
|
+
# Implementations must provide a JWK set containing both signing and encryption keys
|
|
7
|
+
module RelyingPartyJwksStorage
|
|
8
|
+
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
require_relative 'omniauth_strong_auth_oidc/version'
|
|
3
|
+
require_relative 'omniauth_strong_auth_oidc/entity_statement'
|
|
4
|
+
require_relative 'omniauth_strong_auth_oidc/entity_statement_fetcher'
|
|
5
|
+
require_relative 'omniauth_strong_auth_oidc/jwks_cache'
|
|
6
|
+
require_relative 'omniauth_strong_auth_oidc/jwks_fetcher'
|
|
7
|
+
require_relative 'omniauth_strong_auth_oidc/relying_party_jwks_storage'
|
|
8
|
+
require_relative 'omniauth_strong_auth_oidc/relying_party_entity_statement_generator'
|
|
9
|
+
require_relative 'omniauth_strong_auth_oidc/relying_party_jwks_generator'
|
|
10
|
+
require_relative 'omniauth/strategies/strong_auth_oidc'
|
|
11
|
+
|
|
12
|
+
module OmniauthStrongAuthOidc
|
|
13
|
+
# gem version from `lib/omniauth_strong_auth_oidc/version.rb`
|
|
14
|
+
# kept empty module body intentionally
|
|
15
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/omniauth_strong_auth_oidc/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "omniauth_strong_auth_oidc"
|
|
7
|
+
spec.version = OmniauthStrongAuthOidc::VERSION
|
|
8
|
+
spec.authors = ["Kisko Labs", "Dmitry Gusev"]
|
|
9
|
+
spec.email = ["dmitry@kiskolabs.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = %q{OmniAuth strategy for Strong Auth OIDC}
|
|
12
|
+
spec.description = File.exist?("README.md") ? File.read("README.md") : spec.summary
|
|
13
|
+
spec.homepage = "https://github.com/kiskolabs/omniauth_strong_auth_oidc"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^spec/}) }
|
|
17
|
+
spec.require_paths = ["lib"]
|
|
18
|
+
|
|
19
|
+
spec.add_dependency "omniauth-oauth2", "~> 1.8"
|
|
20
|
+
spec.add_dependency "jwt", "~> 2.10"
|
|
21
|
+
spec.add_dependency "jwe", "~> 1.1"
|
|
22
|
+
spec.add_dependency "http", "~> 5.0"
|
|
23
|
+
|
|
24
|
+
spec.add_development_dependency "bundler"
|
|
25
|
+
spec.add_development_dependency "rake"
|
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
27
|
+
spec.add_development_dependency "activesupport", "~> 7.0"
|
|
28
|
+
|
|
29
|
+
spec.metadata = {
|
|
30
|
+
"allowed_push_host" => "https://rubygems.org"
|
|
31
|
+
}
|
|
32
|
+
end
|