omniauth-swedbank 0.3.0 → 0.4.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/.github/workflows/ruby.yml +9 -9
- data/README.md +41 -3
- data/docs/migration_008_to_009.md +89 -0
- data/lib/omniauth/strategies/swedbank.rb +128 -30
- data/lib/omniauth/swedbank/version.rb +1 -1
- data/omniauth-swedbank.gemspec +1 -1
- data/spec/certs/response.v009.public.pem +17 -0
- data/spec/omniauth/strategies/swedbank_spec.rb +391 -167
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 528ab8c0fad20b14c0e37c6c5ed9a027e801aebd8c365d88d3278a450f3f008b
|
|
4
|
+
data.tar.gz: afd969593c671e47716fe4ab9a8aec56f2d4af37ba2422e6f8c898830d6bd7fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c4cd649797d99a39ae36c9874866a37c0622091232059723196eecfb39be7b1002e38fcde0d762f053e5031df280118a4ecc914bd10c45bb3e7fec75d87737f2
|
|
7
|
+
data.tar.gz: a27486b76f6f6dcd109d86efa93756df72694a146c0fa962821a6cdb4a160fcef89897839a2b12c97b66e5cbdf2f12e34600ee792e209430241d6614a1e220f8
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -11,14 +11,14 @@ jobs:
|
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
strategy:
|
|
13
13
|
matrix:
|
|
14
|
-
ruby-version: [
|
|
14
|
+
ruby-version: ["3.1", "3.2", "3.3", "3.4", "4.0"]
|
|
15
15
|
|
|
16
16
|
steps:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- name: Set up Ruby
|
|
19
|
+
uses: ruby/setup-ruby@v1
|
|
20
|
+
with:
|
|
21
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
22
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: bundle exec rspec
|
data/README.md
CHANGED
|
@@ -26,6 +26,10 @@ Or install it yourself as:
|
|
|
26
26
|
|
|
27
27
|
$ gem install omniauth-rails_csrf_protection omniauth-swedbank
|
|
28
28
|
|
|
29
|
+
## v009 Migration
|
|
30
|
+
|
|
31
|
+
**Swedbank will shut down banklink protocol v008 on 2026-06-02.** See [Migration Guide](docs/migration_008_to_009.md) for details.
|
|
32
|
+
|
|
29
33
|
## Usage
|
|
30
34
|
|
|
31
35
|
Here's a quick example, adding the middleware to a Rails app
|
|
@@ -37,13 +41,47 @@ Rails.application.config.middleware.use OmniAuth::Builder do
|
|
|
37
41
|
File.read("path/to/private.key"),
|
|
38
42
|
File.read("path/to/bank.crt"),
|
|
39
43
|
ENV['SWEDBANK_SND_ID'],
|
|
40
|
-
ENV['SWEDBANK_REC_ID']
|
|
44
|
+
ENV['SWEDBANK_REC_ID'],
|
|
45
|
+
version: '009'
|
|
41
46
|
end
|
|
42
47
|
```
|
|
43
48
|
|
|
49
|
+
The `version` option defaults to `'008'` for backward compatibility. Set it to `'009'` when you're ready to migrate (requires a new bank certificate from [banklink.swedbank.com](https://banklink.swedbank.com/public/resources/bank-certificates/009)).
|
|
50
|
+
|
|
44
51
|
## Auth Hash
|
|
45
52
|
|
|
46
|
-
|
|
53
|
+
### v009
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
{
|
|
57
|
+
provider: 'swedbank',
|
|
58
|
+
uid: '374042-80367',
|
|
59
|
+
info: {
|
|
60
|
+
full_name: 'ARNIS RAITUMS',
|
|
61
|
+
country: 'LV'
|
|
62
|
+
},
|
|
63
|
+
extra: {
|
|
64
|
+
raw_info: {
|
|
65
|
+
VK_SERVICE: '3013',
|
|
66
|
+
VK_VERSION: '009',
|
|
67
|
+
VK_DATETIME: '2026-04-29T12:00:00+0300',
|
|
68
|
+
VK_SND_ID: 'SWEDBANK_LV',
|
|
69
|
+
VK_REC_ID: 'MPLMT',
|
|
70
|
+
VK_NONCE: '20170425114529204413',
|
|
71
|
+
VK_USER_NAME: 'ARNIS RAITUMS',
|
|
72
|
+
VK_USER_ID: '374042-80367',
|
|
73
|
+
VK_COUNTRY: 'LV',
|
|
74
|
+
VK_OTHER: '',
|
|
75
|
+
VK_TOKEN: '7',
|
|
76
|
+
VK_RID: '',
|
|
77
|
+
VK_MAC: 'qrEMRf6YV...',
|
|
78
|
+
VK_ENCODING: 'UTF-8'
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### v008 (deprecated)
|
|
47
85
|
|
|
48
86
|
```ruby
|
|
49
87
|
{
|
|
@@ -61,7 +99,7 @@ Here's an example Auth Hash available in `request.env['omniauth.auth']`:
|
|
|
61
99
|
VK_NONCE: '20170425114529204413',
|
|
62
100
|
VK_INFO: 'ISIK:090482-12549;NIMI:DACE ĀBOLA',
|
|
63
101
|
VK_MAC: 'qrEMRf6YV...',
|
|
64
|
-
VK_ENCODING: 'UTF-8
|
|
102
|
+
VK_ENCODING: 'UTF-8'
|
|
65
103
|
}
|
|
66
104
|
}
|
|
67
105
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Migration Guide: v008 to v009
|
|
2
|
+
|
|
3
|
+
## Deadline
|
|
4
|
+
|
|
5
|
+
Swedbank will shut down banklink protocol v008 on **2026-06-02**. After this date, all v008 authentication requests will be rejected by the bank.
|
|
6
|
+
|
|
7
|
+
## What Changed in v009
|
|
8
|
+
|
|
9
|
+
| Aspect | v008 | v009 |
|
|
10
|
+
|--------|------|------|
|
|
11
|
+
| Signing algorithm | SHA-1 | SHA-512 |
|
|
12
|
+
| Request service code | 4002 | 4012 |
|
|
13
|
+
| Response service code | 3003 | 3013 |
|
|
14
|
+
| VK_SND_ID in response | HP | SWEDBANK_LV |
|
|
15
|
+
| Bank certificate | Country-specific (LV) | Unified Baltic |
|
|
16
|
+
| Min. key strength | 1024 bits | 2048 bits (recommended 4096) |
|
|
17
|
+
| Response user data | VK_INFO (combined string) | VK_USER_NAME, VK_USER_ID, VK_COUNTRY, VK_OTHER, VK_TOKEN |
|
|
18
|
+
| New request fields | - | VK_DATETIME, VK_RID |
|
|
19
|
+
|
|
20
|
+
## Migration Steps
|
|
21
|
+
|
|
22
|
+
### 1. Update the gem
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bundle update omniauth-swedbank
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
With the default configuration (`version: '008'`), everything continues to work as before. You will see a deprecation warning in logs.
|
|
29
|
+
|
|
30
|
+
### 2. Download the new bank certificate
|
|
31
|
+
|
|
32
|
+
The v009 protocol uses a new unified Baltic certificate. Download it from:
|
|
33
|
+
|
|
34
|
+
https://banklink.swedbank.com/public/resources/bank-certificates/009
|
|
35
|
+
|
|
36
|
+
Replace your existing Swedbank public certificate file with the new one.
|
|
37
|
+
|
|
38
|
+
### 3. Check your private key
|
|
39
|
+
|
|
40
|
+
Your RSA private key must be at least **2048 bits** (recommended: **4096 bits**). Keys must be regenerated every **24 months**. Check your key size with:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
openssl rsa -in your_private_key.pem -text -noout | head -1
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If it shows less than 2048 bits or is older than 2 years, generate a new keypair. See [Keypair Change Instruction (PDF)](https://swedbank.lv/static/pdf/business/d2d/collection/keypair_change_instruction_LV.pdf) for detailed steps. Two options:
|
|
47
|
+
|
|
48
|
+
- **Option A:** Use Swedbank's built-in keypair generator via the [support page](https://www.swedbank.lv/business/cash/banklink/integrate) (recommended). Log in, go to "My agreements", click "Update key" -> "Generate new key". Download and save the private key immediately — the bank does not store it.
|
|
49
|
+
- **Option B:** Generate your own keypair and upload the public key via self-service or email it to cashmanagement@swedbank.lv.
|
|
50
|
+
|
|
51
|
+
Note: When replacing keys, you can choose a transition period (up to 7 days) during which both old and new keys are valid.
|
|
52
|
+
|
|
53
|
+
### 4. Update your provider configuration
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
# Before (v008 - default)
|
|
57
|
+
provider :swedbank,
|
|
58
|
+
File.read("path/to/private.key"),
|
|
59
|
+
File.read("path/to/old_bank.crt"),
|
|
60
|
+
ENV['SWEDBANK_SND_ID'],
|
|
61
|
+
ENV['SWEDBANK_REC_ID']
|
|
62
|
+
|
|
63
|
+
# After (v009)
|
|
64
|
+
provider :swedbank,
|
|
65
|
+
File.read("path/to/private.key"),
|
|
66
|
+
File.read("path/to/new_baltic_bank.crt"),
|
|
67
|
+
ENV['SWEDBANK_SND_ID'],
|
|
68
|
+
ENV['SWEDBANK_REC_ID'],
|
|
69
|
+
version: '009'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 5. Update your callback handling (if applicable)
|
|
73
|
+
|
|
74
|
+
If your application reads user data from the auth hash, note these changes:
|
|
75
|
+
|
|
76
|
+
**v008:** User ID and name were parsed from the `VK_INFO` field (`ISIK:123456-12345;NIMI:John Doe`).
|
|
77
|
+
|
|
78
|
+
**v009:** User data comes in separate fields:
|
|
79
|
+
- `auth.uid` - reads from `VK_USER_ID` directly
|
|
80
|
+
- `auth.info.full_name` - reads from `VK_USER_NAME` directly
|
|
81
|
+
- `auth.info.country` - new field from `VK_COUNTRY` (e.g., `LV`)
|
|
82
|
+
- `auth.extra.raw_info` - contains all response parameters including `VK_TOKEN`, `VK_OTHER`, `VK_RID`
|
|
83
|
+
|
|
84
|
+
If you only use `auth.uid` and `auth.info.full_name`, no changes are needed in your application code.
|
|
85
|
+
|
|
86
|
+
## Reference
|
|
87
|
+
|
|
88
|
+
- [Swedbank comparison PDF](https://www.swedbank.lv/static/business/banklink/LV_Authentication_008_vs_009_instruction.pdf)
|
|
89
|
+
- [Bank certificates for v009](https://banklink.swedbank.com/public/resources/bank-certificates/009)
|
|
@@ -6,8 +6,10 @@ module OmniAuth
|
|
|
6
6
|
class Swedbank
|
|
7
7
|
include OmniAuth::Strategy
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
V008_AUTH_SERVICE = '4002'
|
|
10
|
+
V008_RESPONSE_SERVICE = '3003'
|
|
11
|
+
V009_AUTH_SERVICE = '4012'
|
|
12
|
+
V009_RESPONSE_SERVICE = '3013'
|
|
11
13
|
|
|
12
14
|
def self.render_nonce?
|
|
13
15
|
defined?(ActionDispatch::ContentSecurityPolicy::Request) != nil
|
|
@@ -26,40 +28,96 @@ module OmniAuth
|
|
|
26
28
|
|
|
27
29
|
option :name, 'swedbank'
|
|
28
30
|
option :site, 'https://www.swedbank.lv/banklink'
|
|
31
|
+
option :version, '008'
|
|
32
|
+
|
|
33
|
+
SUPPORTED_VERSIONS = %w[008 009].freeze
|
|
34
|
+
|
|
35
|
+
def version_009?
|
|
36
|
+
options.version == '009'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def invalid_version?
|
|
40
|
+
!SUPPORTED_VERSIONS.include?(options.version)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def auth_service
|
|
44
|
+
version_009? ? V009_AUTH_SERVICE : V008_AUTH_SERVICE
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def response_service
|
|
48
|
+
version_009? ? V009_RESPONSE_SERVICE : V008_RESPONSE_SERVICE
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def digest
|
|
52
|
+
version_009? ? OpenSSL::Digest::SHA512.new : OpenSSL::Digest::SHA1.new
|
|
53
|
+
end
|
|
29
54
|
|
|
30
55
|
def stamp
|
|
31
56
|
return @stamp if @stamp
|
|
32
57
|
@stamp = Time.now.strftime('%Y%m%d%H%M%S') + SecureRandom.random_number(999999).to_s.rjust(6, '0')
|
|
33
58
|
end
|
|
34
59
|
|
|
60
|
+
def datetime
|
|
61
|
+
@datetime ||= Time.now.strftime('%Y-%m-%dT%H:%M:%S%z')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def rid
|
|
65
|
+
''
|
|
66
|
+
end
|
|
67
|
+
|
|
35
68
|
def prepend_length(value)
|
|
36
69
|
# prepend length to string in 0xx format
|
|
37
70
|
[ value.to_s.length.to_s.rjust(3, '0'), value.dup.to_s.force_encoding('ascii')].join
|
|
38
71
|
end
|
|
39
72
|
|
|
40
73
|
def signature_input
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
74
|
+
fields = if version_009?
|
|
75
|
+
[
|
|
76
|
+
auth_service, # VK_SERVICE
|
|
77
|
+
options.version, # VK_VERSION
|
|
78
|
+
options.snd_id, # VK_SND_ID
|
|
79
|
+
options.rec_id, # VK_REC_ID
|
|
80
|
+
stamp, # VK_NONCE
|
|
81
|
+
callback_url, # VK_RETURN
|
|
82
|
+
datetime, # VK_DATETIME
|
|
83
|
+
rid # VK_RID
|
|
84
|
+
]
|
|
85
|
+
else
|
|
86
|
+
[
|
|
87
|
+
auth_service, # VK_SERVICE
|
|
88
|
+
options.version, # VK_VERSION
|
|
89
|
+
options.snd_id, # VK_SND_ID
|
|
90
|
+
options.rec_id, # VK_REC_ID
|
|
91
|
+
stamp, # VK_NONCE
|
|
92
|
+
callback_url # VK_RETURN
|
|
93
|
+
]
|
|
94
|
+
end
|
|
95
|
+
fields.map{|v| prepend_length(v)}.join
|
|
49
96
|
end
|
|
50
97
|
|
|
51
98
|
def signature(priv_key)
|
|
52
|
-
Base64.encode64(priv_key.sign(
|
|
99
|
+
Base64.encode64(priv_key.sign(digest, signature_input))
|
|
53
100
|
end
|
|
54
101
|
|
|
55
102
|
uid do
|
|
56
|
-
|
|
103
|
+
if version_009?
|
|
104
|
+
request.params['VK_USER_ID']
|
|
105
|
+
else
|
|
106
|
+
request.params['VK_INFO'].match(/ISIK:(\d{6}\-\d{5})/)[1]
|
|
107
|
+
end
|
|
57
108
|
end
|
|
58
109
|
|
|
59
110
|
info do
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
111
|
+
if version_009?
|
|
112
|
+
{
|
|
113
|
+
full_name: request.params['VK_USER_NAME'],
|
|
114
|
+
country: request.params['VK_COUNTRY']
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
{
|
|
118
|
+
full_name: request.params['VK_INFO'].match(/NIMI:(.+)/)[1]
|
|
119
|
+
}
|
|
120
|
+
end
|
|
63
121
|
end
|
|
64
122
|
|
|
65
123
|
extra do
|
|
@@ -67,17 +125,22 @@ module OmniAuth
|
|
|
67
125
|
end
|
|
68
126
|
|
|
69
127
|
def callback_phase
|
|
128
|
+
if invalid_version?
|
|
129
|
+
return fail!(:unsupported_version_err,
|
|
130
|
+
ArgumentError.new("Unsupported banklink version '#{options.version}'. Supported: #{SUPPORTED_VERSIONS.join(', ')}"))
|
|
131
|
+
end
|
|
132
|
+
|
|
70
133
|
begin
|
|
71
134
|
pub_key = OpenSSL::X509::Certificate.new(options.public_key).public_key
|
|
72
135
|
rescue => e
|
|
73
136
|
return fail!(:public_key_load_err, e)
|
|
74
137
|
end
|
|
75
138
|
|
|
76
|
-
if request.params['VK_SERVICE'] !=
|
|
139
|
+
if request.params['VK_SERVICE'] != response_service
|
|
77
140
|
return fail!(:unsupported_response_service_err)
|
|
78
141
|
end
|
|
79
142
|
|
|
80
|
-
if request.params['VK_VERSION'] !=
|
|
143
|
+
if request.params['VK_VERSION'] != options.version
|
|
81
144
|
return fail!(:unsupported_response_version_err)
|
|
82
145
|
end
|
|
83
146
|
|
|
@@ -85,18 +148,35 @@ module OmniAuth
|
|
|
85
148
|
return fail!(:unsupported_response_encoding_err)
|
|
86
149
|
end
|
|
87
150
|
|
|
88
|
-
sig_str =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
151
|
+
sig_str = if version_009?
|
|
152
|
+
[
|
|
153
|
+
request.params['VK_SERVICE'],
|
|
154
|
+
request.params['VK_VERSION'],
|
|
155
|
+
request.params['VK_DATETIME'],
|
|
156
|
+
request.params['VK_SND_ID'],
|
|
157
|
+
request.params['VK_REC_ID'],
|
|
158
|
+
request.params['VK_NONCE'],
|
|
159
|
+
request.params['VK_USER_NAME'],
|
|
160
|
+
request.params['VK_USER_ID'],
|
|
161
|
+
request.params['VK_COUNTRY'],
|
|
162
|
+
request.params['VK_OTHER'],
|
|
163
|
+
request.params['VK_TOKEN'],
|
|
164
|
+
request.params['VK_RID']
|
|
165
|
+
].map{|v| prepend_length(v)}.join
|
|
166
|
+
else
|
|
167
|
+
[
|
|
168
|
+
request.params['VK_SERVICE'],
|
|
169
|
+
request.params['VK_VERSION'],
|
|
170
|
+
request.params['VK_SND_ID'],
|
|
171
|
+
request.params['VK_REC_ID'],
|
|
172
|
+
request.params['VK_NONCE'],
|
|
173
|
+
request.params['VK_INFO']
|
|
174
|
+
].map{|v| prepend_length(v)}.join
|
|
175
|
+
end
|
|
96
176
|
|
|
97
177
|
raw_signature = Base64.decode64(request.params['VK_MAC'])
|
|
98
178
|
|
|
99
|
-
if !pub_key.verify(
|
|
179
|
+
if !pub_key.verify(digest, raw_signature, sig_str)
|
|
100
180
|
return fail!(:invalid_response_signature_err)
|
|
101
181
|
end
|
|
102
182
|
|
|
@@ -104,19 +184,30 @@ module OmniAuth
|
|
|
104
184
|
end
|
|
105
185
|
|
|
106
186
|
def request_phase
|
|
187
|
+
if invalid_version?
|
|
188
|
+
return fail!(:unsupported_version_err,
|
|
189
|
+
ArgumentError.new("Unsupported banklink version '#{options.version}'. Supported: #{SUPPORTED_VERSIONS.join(', ')}"))
|
|
190
|
+
end
|
|
191
|
+
|
|
107
192
|
begin
|
|
108
193
|
priv_key = OpenSSL::PKey::RSA.new(options.private_key)
|
|
109
194
|
rescue => e
|
|
110
195
|
return fail!(:private_key_load_err, e)
|
|
111
196
|
end
|
|
112
197
|
|
|
198
|
+
unless version_009?
|
|
199
|
+
warn "[DEPRECATION] omniauth-swedbank: Swedbank banklink v008 will be shut down on 2026-06-02. " \
|
|
200
|
+
"Please migrate to v009 by setting `version: '009'` in your provider config. " \
|
|
201
|
+
"See https://www.swedbank.lv/static/business/banklink/LV_Authentication_008_vs_009_instruction.pdf"
|
|
202
|
+
end
|
|
203
|
+
|
|
113
204
|
set_locale_from_query_param
|
|
114
205
|
|
|
115
206
|
form = OmniAuth::Form.new(:title => I18n.t('omniauth.swedbank.please_wait'), :url => options.site)
|
|
116
207
|
|
|
117
|
-
{
|
|
118
|
-
'VK_SERVICE' =>
|
|
119
|
-
'VK_VERSION' =>
|
|
208
|
+
params = {
|
|
209
|
+
'VK_SERVICE' => auth_service,
|
|
210
|
+
'VK_VERSION' => options.version,
|
|
120
211
|
'VK_SND_ID' => options.snd_id,
|
|
121
212
|
'VK_REC_ID' => options.rec_id,
|
|
122
213
|
'VK_NONCE' => stamp,
|
|
@@ -124,7 +215,14 @@ module OmniAuth
|
|
|
124
215
|
'VK_MAC' => signature(priv_key),
|
|
125
216
|
'VK_LANG' => resolve_bank_ui_language,
|
|
126
217
|
'VK_ENCODING' => 'UTF-8'
|
|
127
|
-
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if version_009?
|
|
221
|
+
params['VK_DATETIME'] = datetime
|
|
222
|
+
params['VK_RID'] = rid
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
params.each do |name, val|
|
|
128
226
|
form.html "<input type=\"hidden\" name=\"#{name}\" value=\"#{escape(val)}\" />"
|
|
129
227
|
end
|
|
130
228
|
|
data/omniauth-swedbank.gemspec
CHANGED
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
19
|
spec.require_paths = ['lib']
|
|
20
20
|
|
|
21
|
-
spec.required_ruby_version = '>=
|
|
21
|
+
spec.required_ruby_version = '>= 3.1'
|
|
22
22
|
|
|
23
23
|
spec.add_runtime_dependency 'omniauth', '~> 2.1'
|
|
24
24
|
spec.add_runtime_dependency 'i18n'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIICoTCCAYmgAwIBAgIBATANBgkqhkiG9w0BAQ0FADAUMRIwEAYDVQQDDAlUZXN0
|
|
3
|
+
IFYwMDkwHhcNMjYwNDI5MDkyNjQ5WhcNMjcwNDI5MDkyNjQ5WjAUMRIwEAYDVQQD
|
|
4
|
+
DAlUZXN0IFYwMDkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtZLYp
|
|
5
|
+
KJNe16Fp2S/EVyyz0aFdPhBwgn+707JW/a8EM1m0rryNUbwZpQqZ2wzmPy6r/D13
|
|
6
|
+
UssJpGVg74+LQ8GZI+mgiL+U7nY95111XhOQ/B258i40HAwf3WFVqDKmigYxXU+O
|
|
7
|
+
0hbuUwXmT7qP3sRNgNLChn6BFDV6c1f/TYkiSZrSj45HQsqDekCIZdy6CvzV8m5I
|
|
8
|
+
JV3TJWshoEj4HPJI7YEkgjc3nzwUsKiLi9k6FsLSiveFBaky+mhYBlJUtET7KEwu
|
|
9
|
+
raHw7LkCn1FoCSodgxeHF9IFCCyKTI7VKETB4w0TYLKCbMOjtON0ifCx+H/w8LwP
|
|
10
|
+
YrhXTF6rjcdL3A9jAgMBAAEwDQYJKoZIhvcNAQENBQADggEBAErV4sBKLwmUvjOT
|
|
11
|
+
S8Kjw+BwTf0NU/yOpQS1kCSTD5gn9OOqDirOQe7Yj1dyGWMDmfGy4Jw7xcMSRTTq
|
|
12
|
+
TCKS7poTkFa8mXRPm+kFqw1Hy3U6/MsswZCBxiIkGEltKytXe+AdLVY2uM46/j6W
|
|
13
|
+
D6Rt0vgcBcj//h2et4f8GDMs4s1ndKp8o0rnggSQCcgC2yLMNL8AdSxwHj7eGDiv
|
|
14
|
+
cCWspZCXq7SGOoMC3jhhNOYt9WjH+/Aj/FXDRu2iguaG4I4qBGJSmY2i4WQT/XJv
|
|
15
|
+
sXPCrHoN0oGrmzX/V5/20AEOhS6oBLCIMfBeR4iRAicUW9mUnVj+PqigZkXGSArB
|
|
16
|
+
tkkI44U=
|
|
17
|
+
-----END CERTIFICATE-----
|
|
@@ -6,210 +6,434 @@ describe OmniAuth::Strategies::Swedbank do
|
|
|
6
6
|
|
|
7
7
|
PRIVATE_KEY = File.read(File.join(RSpec.configuration.cert_folder, 'request.private.pem'))
|
|
8
8
|
PUBLIC_KEY = File.read(File.join(RSpec.configuration.cert_folder, 'response.public.pem'))
|
|
9
|
-
|
|
10
|
-
let(:app){ Rack::Builder.new do |b|
|
|
11
|
-
b.use Rack::Session::Cookie, {secret: '5242e6bd9daf0e9645c2d4e22b11ba8cee0bed44439906d5f1bd5dad409d8637'}
|
|
12
|
-
b.use(OmniAuth::Strategies::Swedbank, PRIVATE_KEY, PUBLIC_KEY, 'MY_SND_ID', 'MY_REC_ID')
|
|
13
|
-
b.run lambda{|env| [404, {}, ['Not Found']]}
|
|
14
|
-
end.to_app }
|
|
9
|
+
PUBLIC_KEY_V009 = File.read(File.join(RSpec.configuration.cert_folder, 'response.v009.public.pem'))
|
|
15
10
|
|
|
16
11
|
let(:token){ Rack::Protection::AuthenticityToken.random_token }
|
|
17
12
|
|
|
18
13
|
let(:last_response_nonce) { last_response.body.match(/name="VK_NONCE" value="([^"]*)"/)[1] }
|
|
19
14
|
let(:last_response_mac) { last_response.body.match(/name="VK_MAC" value="([^"]*)"/)[1] }
|
|
20
15
|
|
|
21
|
-
context '
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
'
|
|
33
|
-
|
|
34
|
-
'
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
16
|
+
context 'v008 (default)' do
|
|
17
|
+
let(:app){ Rack::Builder.new do |b|
|
|
18
|
+
b.use Rack::Session::Cookie, {secret: '5242e6bd9daf0e9645c2d4e22b11ba8cee0bed44439906d5f1bd5dad409d8637'}
|
|
19
|
+
b.use(OmniAuth::Strategies::Swedbank, PRIVATE_KEY, PUBLIC_KEY, 'MY_SND_ID', 'MY_REC_ID')
|
|
20
|
+
b.run lambda{|env| [404, {}, ['Not Found']]}
|
|
21
|
+
end.to_app }
|
|
22
|
+
|
|
23
|
+
context 'request phase' do
|
|
24
|
+
EXPECTED_VALUES_V008 = {
|
|
25
|
+
'VK_SERVICE' => '4002',
|
|
26
|
+
'VK_VERSION' => '008',
|
|
27
|
+
'VK_SND_ID' => 'MY_SND_ID',
|
|
28
|
+
'VK_REC_ID' => 'MY_REC_ID',
|
|
29
|
+
'VK_RETURN' => 'http://example.org/auth/swedbank/callback'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
before(:each) do
|
|
33
|
+
post(
|
|
34
|
+
'/auth/swedbank',
|
|
35
|
+
{},
|
|
36
|
+
'rack.session' => {csrf: token},
|
|
37
|
+
'HTTP_X_CSRF_TOKEN' => token
|
|
38
|
+
)
|
|
39
|
+
end
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
it 'displays a single form' do
|
|
42
|
+
expect(last_response.status).to eq(200)
|
|
43
|
+
expect(last_response.body.scan('<form').size).to eq(1)
|
|
44
|
+
end
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
it 'has JavaScript code to submit the form after it is created' do
|
|
47
|
+
expect(last_response.body).to be_include('</form><script type="text/javascript">document.forms[0].submit();</script>')
|
|
48
|
+
end
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
EXPECTED_VALUES_V008.each_pair do |k,v|
|
|
51
|
+
it "has hidden input field #{k} => #{v}" do
|
|
52
|
+
expect(last_response.body.scan(
|
|
53
|
+
"<input type=\"hidden\" name=\"#{k}\" value=\"#{v}\"").size).to eq(1)
|
|
54
|
+
end
|
|
52
55
|
end
|
|
53
|
-
end
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
it 'has a 20 byte long nonce field value' do
|
|
58
|
+
expect(last_response_nonce.bytesize).to eq(20)
|
|
59
|
+
end
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
it 'has a correct VK_MAC signature' do
|
|
62
|
+
sig_str =
|
|
63
|
+
'0044002' + # VK_SERVICE
|
|
64
|
+
'003008' + # VK_VERSION
|
|
65
|
+
'009MY_SND_ID' + # VK_SND_ID
|
|
66
|
+
'009MY_REC_ID' + # VK_REC_ID
|
|
67
|
+
"020#{last_response_nonce}" + # VK_NONCE
|
|
68
|
+
"041#{EXPECTED_VALUES_V008['VK_RETURN']}" # V_RETURN
|
|
69
|
+
|
|
70
|
+
private_key = OpenSSL::PKey::RSA.new(PRIVATE_KEY)
|
|
71
|
+
expected_mac = Base64.encode64(private_key.sign(OpenSSL::Digest::SHA1.new, sig_str))
|
|
72
|
+
expect(last_response_mac).to eq(expected_mac)
|
|
73
|
+
end
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
expect(last_response.body).to be_include("action='https://www.swedbank.lv/banklink'")
|
|
75
|
+
it 'does not include VK_DATETIME field' do
|
|
76
|
+
expect(last_response.body).not_to include('name="VK_DATETIME"')
|
|
76
77
|
end
|
|
77
78
|
|
|
78
|
-
it '
|
|
79
|
-
expect(last_response.body).
|
|
79
|
+
it 'does not include VK_RID field' do
|
|
80
|
+
expect(last_response.body).not_to include('name="VK_RID"')
|
|
80
81
|
end
|
|
81
|
-
end
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
it 'outputs a deprecation warning' do
|
|
84
|
+
expect { post('/auth/swedbank', {}, 'rack.session' => {csrf: token}, 'HTTP_X_CSRF_TOKEN' => token) }
|
|
85
|
+
.to output(/DEPRECATION.*v008.*2026-06-02/).to_stderr
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
context 'with default options' do
|
|
89
|
+
it 'has the default action tag value' do
|
|
90
|
+
expect(last_response.body).to be_include("action='https://www.swedbank.lv/banklink'")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'has the default VK_LANG value' do
|
|
94
|
+
expect(last_response.body).to be_include("action='https://www.swedbank.lv/banklink'")
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context 'with custom options' do
|
|
99
|
+
let(:app){ Rack::Builder.new do |b|
|
|
100
|
+
b.use Rack::Session::Cookie, {secret: '5242e6bd9daf0e9645c2d4e22b11ba8cee0bed44439906d5f1bd5dad409d8637'}
|
|
101
|
+
b.use(OmniAuth::Strategies::Swedbank, PRIVATE_KEY, PUBLIC_KEY, 'MY_SND_ID', 'MY_REC_ID',
|
|
102
|
+
site: 'https://test.lv/banklink')
|
|
103
|
+
b.run lambda{|env| [404, {}, ['Not Found']]}
|
|
104
|
+
end.to_app }
|
|
105
|
+
|
|
106
|
+
it 'has the custom action tag value' do
|
|
107
|
+
expect(last_response.body).to be_include("action='https://test.lv/banklink'")
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
context 'with non-existant private key files' do
|
|
112
|
+
let(:app){ Rack::Builder.new do |b|
|
|
113
|
+
b.use Rack::Session::Cookie, {secret: '5242e6bd9daf0e9645c2d4e22b11ba8cee0bed44439906d5f1bd5dad409d8637'}
|
|
114
|
+
b.use(OmniAuth::Strategies::Swedbank, 'missing-private-key-file.pem', PUBLIC_KEY, 'MY_SND_ID', 'MY_REC_ID')
|
|
115
|
+
b.run lambda{|env| [404, {}, ['Not Found']]}
|
|
116
|
+
end.to_app }
|
|
117
|
+
|
|
118
|
+
it 'redirects to /auth/failure with appropriate query params' do
|
|
119
|
+
expect(last_response.status).to eq(302)
|
|
120
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=private_key_load_err&strategy=swedbank')
|
|
121
|
+
end
|
|
122
|
+
end
|
|
90
123
|
|
|
91
|
-
|
|
92
|
-
|
|
124
|
+
context 'with invalid version' do
|
|
125
|
+
let(:app){ Rack::Builder.new do |b|
|
|
126
|
+
b.use Rack::Session::Cookie, {secret: '5242e6bd9daf0e9645c2d4e22b11ba8cee0bed44439906d5f1bd5dad409d8637'}
|
|
127
|
+
b.use(OmniAuth::Strategies::Swedbank, PRIVATE_KEY, PUBLIC_KEY, 'MY_SND_ID', 'MY_REC_ID',
|
|
128
|
+
version: '010')
|
|
129
|
+
b.run lambda{|env| [404, {}, ['Not Found']]}
|
|
130
|
+
end.to_app }
|
|
131
|
+
|
|
132
|
+
it 'fails with unsupported_version_err on request phase' do
|
|
133
|
+
post('/auth/swedbank', {}, 'rack.session' => {csrf: token}, 'HTTP_X_CSRF_TOKEN' => token)
|
|
134
|
+
expect(last_response.status).to eq(302)
|
|
135
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=unsupported_version_err&strategy=swedbank')
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'fails with unsupported_version_err on callback phase' do
|
|
139
|
+
post '/auth/swedbank/callback', 'VK_SERVICE' => '3003'
|
|
140
|
+
expect(last_response.status).to eq(302)
|
|
141
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=unsupported_version_err&strategy=swedbank')
|
|
142
|
+
end
|
|
93
143
|
end
|
|
94
144
|
end
|
|
95
145
|
|
|
96
|
-
context '
|
|
97
|
-
let(:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
146
|
+
context 'callback phase' do
|
|
147
|
+
let(:auth_hash){ last_request.env['omniauth.auth'] }
|
|
148
|
+
|
|
149
|
+
context 'with valid response' do
|
|
150
|
+
before do
|
|
151
|
+
post '/auth/swedbank/callback',
|
|
152
|
+
'VK_SERVICE' => '3003',
|
|
153
|
+
'VK_VERSION' => '008',
|
|
154
|
+
'VK_SND_ID' => 'HP',
|
|
155
|
+
'VK_REC_ID' => 'MY_REC_ID',
|
|
156
|
+
'VK_NONCE' => 'pXXXlocalhostX3000b41292810c0345a7b3770b1c807bed7a',
|
|
157
|
+
'VK_INFO' => 'ISIK:123456-12345;NIMI:Example User',
|
|
158
|
+
'VK_MAC' => 'cmXyp2My7P9pTgrzqJeg7qH+NPCuyaiGNpQIrcCr6S44w0bH+Ao4WDViqytaPH2vENooVPXDSgOcBqHTg44gJ9FlrhI5StiouHVhjpCcWg+h/ERcyc8w58PjsEmdsd4BIpaGXNyhvcIKdWfNwYA1UCIrmFsPAPWfVeorNxp81E7pvY4p4zsqMF80YZ7/RdOpjrtuXJ4nYJ7d+2fXJKKmUlqArCc786DJdb/z8wVDSNA9BZxnf8EE6s//p9gzqLPAg/T9Xp/2024n2JtC6kwsWF614bn64LEZz5c8owZth6FV+2fjnzHxOiifOe+jc9SRstCLITK6Y0j+6n8auiEZ5g==',
|
|
159
|
+
'VK_LANG' => 'LAT',
|
|
160
|
+
'VK_ENCODING' => 'UTF-8'
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it 'sets the correct uid value in the auth hash' do
|
|
164
|
+
expect(auth_hash.uid).to eq('123456-12345')
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'sets the correct info.full_name value in the auth hash' do
|
|
168
|
+
expect(auth_hash.info.full_name).to eq('Example User')
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
context 'with non-existant public key file' do
|
|
173
|
+
let(:app){ Rack::Builder.new do |b|
|
|
174
|
+
b.use Rack::Session::Cookie, {secret: '5242e6bd9daf0e9645c2d4e22b11ba8cee0bed44439906d5f1bd5dad409d8637'}
|
|
175
|
+
b.use(OmniAuth::Strategies::Swedbank, PRIVATE_KEY, 'missing-public-key-file.pem', 'MY_SND_ID', 'MY_REC_ID')
|
|
176
|
+
b.run lambda{|env| [404, {}, ['Not Found']]}
|
|
177
|
+
end.to_app }
|
|
178
|
+
|
|
179
|
+
it 'redirects to /auth/failure with appropriate query params' do
|
|
180
|
+
post '/auth/swedbank/callback'
|
|
181
|
+
expect(last_response.status).to eq(302)
|
|
182
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=public_key_load_err&strategy=swedbank')
|
|
183
|
+
end
|
|
184
|
+
end
|
|
102
185
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
186
|
+
context 'with invalid response' do
|
|
187
|
+
it 'detects invalid signature' do
|
|
188
|
+
post '/auth/swedbank/callback',
|
|
189
|
+
'VK_SERVICE' => '3003',
|
|
190
|
+
'VK_VERSION' => '008',
|
|
191
|
+
'VK_SND_ID' => 'HP',
|
|
192
|
+
'VK_REC_ID' => 'MY_REC_ID',
|
|
193
|
+
'VK_NONCE' => 'pXXXlocalhostX3000b41292810c0345a7b3770b1c807bed7a',
|
|
194
|
+
'VK_INFO' => 'ISIK:123456-12345;NIMI:Example User',
|
|
195
|
+
'VK_MAC' => 'invalid signature',
|
|
196
|
+
'VK_LANG' => 'LAT',
|
|
197
|
+
'VK_ENCODING' => 'UTF-8'
|
|
198
|
+
|
|
199
|
+
expect(last_response.status).to eq(302)
|
|
200
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=invalid_response_signature_err&strategy=swedbank')
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'detects unsupported VK_SERVICE values' do
|
|
204
|
+
post '/auth/swedbank/callback',
|
|
205
|
+
'VK_SERVICE' => '3004',
|
|
206
|
+
'VK_VERSION' => '008',
|
|
207
|
+
'VK_SND_ID' => 'HP',
|
|
208
|
+
'VK_REC_ID' => 'MY_REC_ID',
|
|
209
|
+
'VK_NONCE' => 'pXXXlocalhostX3000b41292810c0345a7b3770b1c807bed7a',
|
|
210
|
+
'VK_INFO' => 'ISIK:123456-12345;NIMI:Example User',
|
|
211
|
+
'VK_MAC' => 'cmXyp2My7P9pTgrzqJeg7qH+NPCuyaiGNpQIrcCr6S44w0bH+Ao4WDViqytaPH2vENooVPXDSgOcBqHTg44gJ9FlrhI5StiouHVhjpCcWg+h/ERcyc8w58PjsEmdsd4BIpaGXNyhvcIKdWfNwYA1UCIrmFsPAPWfVeorNxp81E7pvY4p4zsqMF80YZ7/RdOpjrtuXJ4nYJ7d+2fXJKKmUlqArCc786DJdb/z8wVDSNA9BZxnf8EE6s//p9gzqLPAg/T9Xp/2024n2JtC6kwsWF614bn64LEZz5c8owZth6FV+2fjnzHxOiifOe+jc9SRstCLITK6Y0j+6n8auiEZ5g==',
|
|
212
|
+
'VK_LANG' => 'LAT',
|
|
213
|
+
'VK_ENCODING' => 'UTF-8'
|
|
214
|
+
|
|
215
|
+
expect(last_response.status).to eq(302)
|
|
216
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=unsupported_response_service_err&strategy=swedbank')
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'detects unsupported VK_VERSION values' do
|
|
220
|
+
post '/auth/swedbank/callback',
|
|
221
|
+
'VK_SERVICE' => '3003',
|
|
222
|
+
'VK_VERSION' => '009',
|
|
223
|
+
'VK_SND_ID' => 'HP',
|
|
224
|
+
'VK_REC_ID' => 'MY_REC_ID',
|
|
225
|
+
'VK_NONCE' => 'pXXXlocalhostX3000b41292810c0345a7b3770b1c807bed7a',
|
|
226
|
+
'VK_INFO' => 'ISIK:123456-12345;NIMI:Example User',
|
|
227
|
+
'VK_MAC' => 'cmXyp2My7P9pTgrzqJeg7qH+NPCuyaiGNpQIrcCr6S44w0bH+Ao4WDViqytaPH2vENooVPXDSgOcBqHTg44gJ9FlrhI5StiouHVhjpCcWg+h/ERcyc8w58PjsEmdsd4BIpaGXNyhvcIKdWfNwYA1UCIrmFsPAPWfVeorNxp81E7pvY4p4zsqMF80YZ7/RdOpjrtuXJ4nYJ7d+2fXJKKmUlqArCc786DJdb/z8wVDSNA9BZxnf8EE6s//p9gzqLPAg/T9Xp/2024n2JtC6kwsWF614bn64LEZz5c8owZth6FV+2fjnzHxOiifOe+jc9SRstCLITK6Y0j+6n8auiEZ5g==',
|
|
228
|
+
'VK_LANG' => 'LAT',
|
|
229
|
+
'VK_ENCODING' => 'UTF-8'
|
|
230
|
+
|
|
231
|
+
expect(last_response.status).to eq(302)
|
|
232
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=unsupported_response_version_err&strategy=swedbank')
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'detects unsupported VK_ENCODING values' do
|
|
236
|
+
post '/auth/swedbank/callback',
|
|
237
|
+
'VK_SERVICE' => '3003',
|
|
238
|
+
'VK_VERSION' => '008',
|
|
239
|
+
'VK_SND_ID' => 'HP',
|
|
240
|
+
'VK_REC_ID' => 'MY_REC_ID',
|
|
241
|
+
'VK_NONCE' => 'pXXXlocalhostX3000b41292810c0345a7b3770b1c807bed7a',
|
|
242
|
+
'VK_INFO' => 'ISIK:123456-12345;NIMI:Example User',
|
|
243
|
+
'VK_MAC' => 'cmXyp2My7P9pTgrzqJeg7qH+NPCuyaiGNpQIrcCr6S44w0bH+Ao4WDViqytaPH2vENooVPXDSgOcBqHTg44gJ9FlrhI5StiouHVhjpCcWg+h/ERcyc8w58PjsEmdsd4BIpaGXNyhvcIKdWfNwYA1UCIrmFsPAPWfVeorNxp81E7pvY4p4zsqMF80YZ7/RdOpjrtuXJ4nYJ7d+2fXJKKmUlqArCc786DJdb/z8wVDSNA9BZxnf8EE6s//p9gzqLPAg/T9Xp/2024n2JtC6kwsWF614bn64LEZz5c8owZth6FV+2fjnzHxOiifOe+jc9SRstCLITK6Y0j+6n8auiEZ5g==',
|
|
244
|
+
'VK_LANG' => 'LAT',
|
|
245
|
+
'VK_ENCODING' => 'ASCII'
|
|
246
|
+
|
|
247
|
+
expect(last_response.status).to eq(302)
|
|
248
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=unsupported_response_encoding_err&strategy=swedbank')
|
|
249
|
+
end
|
|
106
250
|
end
|
|
107
251
|
end
|
|
108
252
|
end
|
|
109
253
|
|
|
110
|
-
context '
|
|
111
|
-
let(:
|
|
254
|
+
context 'v009' do
|
|
255
|
+
let(:app){ Rack::Builder.new do |b|
|
|
256
|
+
b.use Rack::Session::Cookie, {secret: '5242e6bd9daf0e9645c2d4e22b11ba8cee0bed44439906d5f1bd5dad409d8637'}
|
|
257
|
+
b.use(OmniAuth::Strategies::Swedbank, PRIVATE_KEY, PUBLIC_KEY_V009, 'MY_SND_ID', 'MY_REC_ID',
|
|
258
|
+
version: '009')
|
|
259
|
+
b.run lambda{|env| [404, {}, ['Not Found']]}
|
|
260
|
+
end.to_app }
|
|
261
|
+
|
|
262
|
+
context 'request phase' do
|
|
263
|
+
EXPECTED_VALUES_V009 = {
|
|
264
|
+
'VK_SERVICE' => '4012',
|
|
265
|
+
'VK_VERSION' => '009',
|
|
266
|
+
'VK_SND_ID' => 'MY_SND_ID',
|
|
267
|
+
'VK_REC_ID' => 'MY_REC_ID',
|
|
268
|
+
'VK_RETURN' => 'http://example.org/auth/swedbank/callback'
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
let(:last_response_datetime) { last_response.body.match(/name="VK_DATETIME" value="([^"]*)"/)[1] }
|
|
272
|
+
let(:last_response_rid) { last_response.body.match(/name="VK_RID" value="([^"]*)"/)[1] }
|
|
273
|
+
|
|
274
|
+
before(:each) do
|
|
275
|
+
post(
|
|
276
|
+
'/auth/swedbank',
|
|
277
|
+
{},
|
|
278
|
+
'rack.session' => {csrf: token},
|
|
279
|
+
'HTTP_X_CSRF_TOKEN' => token
|
|
280
|
+
)
|
|
281
|
+
end
|
|
112
282
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
'VK_SERVICE' => '3003',
|
|
117
|
-
'VK_VERSION' => '008',
|
|
118
|
-
'VK_SND_ID' => 'HP',
|
|
119
|
-
'VK_REC_ID' => 'MY_REC_ID',
|
|
120
|
-
'VK_NONCE' => 'pXXXlocalhostX3000b41292810c0345a7b3770b1c807bed7a',
|
|
121
|
-
'VK_INFO' => 'ISIK:123456-12345;NIMI:Example User',
|
|
122
|
-
'VK_MAC' => 'cmXyp2My7P9pTgrzqJeg7qH+NPCuyaiGNpQIrcCr6S44w0bH+Ao4WDViqytaPH2vENooVPXDSgOcBqHTg44gJ9FlrhI5StiouHVhjpCcWg+h/ERcyc8w58PjsEmdsd4BIpaGXNyhvcIKdWfNwYA1UCIrmFsPAPWfVeorNxp81E7pvY4p4zsqMF80YZ7/RdOpjrtuXJ4nYJ7d+2fXJKKmUlqArCc786DJdb/z8wVDSNA9BZxnf8EE6s//p9gzqLPAg/T9Xp/2024n2JtC6kwsWF614bn64LEZz5c8owZth6FV+2fjnzHxOiifOe+jc9SRstCLITK6Y0j+6n8auiEZ5g==',
|
|
123
|
-
'VK_LANG' => 'LAT',
|
|
124
|
-
'VK_ENCODING' => 'UTF-8'
|
|
283
|
+
it 'displays a single form' do
|
|
284
|
+
expect(last_response.status).to eq(200)
|
|
285
|
+
expect(last_response.body.scan('<form').size).to eq(1)
|
|
125
286
|
end
|
|
126
287
|
|
|
127
|
-
|
|
128
|
-
|
|
288
|
+
EXPECTED_VALUES_V009.each_pair do |k,v|
|
|
289
|
+
it "has hidden input field #{k} => #{v}" do
|
|
290
|
+
expect(last_response.body.scan(
|
|
291
|
+
"<input type=\"hidden\" name=\"#{k}\" value=\"#{v}\"").size).to eq(1)
|
|
292
|
+
end
|
|
129
293
|
end
|
|
130
294
|
|
|
131
|
-
it '
|
|
132
|
-
expect(
|
|
295
|
+
it 'has a VK_DATETIME field' do
|
|
296
|
+
expect(last_response_datetime).to match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}/)
|
|
133
297
|
end
|
|
134
|
-
end
|
|
135
298
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
b.use(OmniAuth::Strategies::Swedbank, PRIVATE_KEY, 'missing-public-key-file.pem', 'MY_SND_ID', 'MY_REC_ID')
|
|
140
|
-
b.run lambda{|env| [404, {}, ['Not Found']]}
|
|
141
|
-
end.to_app }
|
|
299
|
+
it 'has a VK_RID field' do
|
|
300
|
+
expect(last_response.body).to include('name="VK_RID"')
|
|
301
|
+
end
|
|
142
302
|
|
|
143
|
-
it '
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
303
|
+
it 'has a 20 byte long nonce field value' do
|
|
304
|
+
expect(last_response_nonce.bytesize).to eq(20)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it 'has a correct VK_MAC signature using SHA-512' do
|
|
308
|
+
sig_str =
|
|
309
|
+
'0044012' + # VK_SERVICE
|
|
310
|
+
'003009' + # VK_VERSION
|
|
311
|
+
'009MY_SND_ID' + # VK_SND_ID
|
|
312
|
+
'009MY_REC_ID' + # VK_REC_ID
|
|
313
|
+
"020#{last_response_nonce}" + # VK_NONCE
|
|
314
|
+
"041#{EXPECTED_VALUES_V009['VK_RETURN']}" + # VK_RETURN
|
|
315
|
+
"#{'%03d' % last_response_datetime.length}#{last_response_datetime}" + # VK_DATETIME
|
|
316
|
+
"000" # VK_RID (empty)
|
|
317
|
+
|
|
318
|
+
private_key = OpenSSL::PKey::RSA.new(PRIVATE_KEY)
|
|
319
|
+
expected_mac = Base64.encode64(private_key.sign(OpenSSL::Digest::SHA512.new, sig_str))
|
|
320
|
+
expect(last_response_mac).to eq(expected_mac)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
it 'does not output a deprecation warning' do
|
|
324
|
+
expect { post('/auth/swedbank', {}, 'rack.session' => {csrf: token}, 'HTTP_X_CSRF_TOKEN' => token) }
|
|
325
|
+
.not_to output(/DEPRECATION/).to_stderr
|
|
147
326
|
end
|
|
148
327
|
end
|
|
149
328
|
|
|
150
|
-
context '
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
'
|
|
174
|
-
'
|
|
175
|
-
'
|
|
176
|
-
'
|
|
177
|
-
'
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
'
|
|
186
|
-
'
|
|
187
|
-
'
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
'
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
'
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
329
|
+
context 'callback phase' do
|
|
330
|
+
let(:auth_hash){ last_request.env['omniauth.auth'] }
|
|
331
|
+
|
|
332
|
+
# Generate a valid v009 MAC for test data
|
|
333
|
+
let(:v009_response_params) do
|
|
334
|
+
nonce = 'pXXXlocalhostX3000b41292810c0345a7b3770b1c807bed7a'
|
|
335
|
+
datetime = '2026-04-29T12:00:00+0300'
|
|
336
|
+
user_name = 'Example User'
|
|
337
|
+
user_id = '123456-12345'
|
|
338
|
+
country = 'LV'
|
|
339
|
+
other = ''
|
|
340
|
+
token_val = '7'
|
|
341
|
+
rid = ''
|
|
342
|
+
|
|
343
|
+
sig_str = [
|
|
344
|
+
'3013', '009', datetime, 'SWEDBANK_LV', 'MY_REC_ID',
|
|
345
|
+
nonce, user_name, user_id, country, other, token_val, rid
|
|
346
|
+
].map{|v| '%03d' % v.to_s.length + v.to_s.dup.force_encoding('ascii')}.join
|
|
347
|
+
|
|
348
|
+
private_key = OpenSSL::PKey::RSA.new(PRIVATE_KEY)
|
|
349
|
+
mac = Base64.encode64(private_key.sign(OpenSSL::Digest::SHA512.new, sig_str))
|
|
350
|
+
|
|
351
|
+
{
|
|
352
|
+
'VK_SERVICE' => '3013',
|
|
353
|
+
'VK_VERSION' => '009',
|
|
354
|
+
'VK_DATETIME' => datetime,
|
|
355
|
+
'VK_SND_ID' => 'SWEDBANK_LV',
|
|
356
|
+
'VK_REC_ID' => 'MY_REC_ID',
|
|
357
|
+
'VK_NONCE' => nonce,
|
|
358
|
+
'VK_USER_NAME' => user_name,
|
|
359
|
+
'VK_USER_ID' => user_id,
|
|
360
|
+
'VK_COUNTRY' => country,
|
|
361
|
+
'VK_OTHER' => other,
|
|
362
|
+
'VK_TOKEN' => token_val,
|
|
363
|
+
'VK_RID' => rid,
|
|
364
|
+
'VK_MAC' => mac,
|
|
365
|
+
'VK_LANG' => 'LAT',
|
|
366
|
+
'VK_ENCODING' => 'UTF-8'
|
|
367
|
+
}
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
context 'with valid response' do
|
|
371
|
+
before do
|
|
372
|
+
post '/auth/swedbank/callback', v009_response_params
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
it 'sets the correct uid value in the auth hash' do
|
|
376
|
+
expect(auth_hash.uid).to eq('123456-12345')
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it 'sets the correct info.full_name value in the auth hash' do
|
|
380
|
+
expect(auth_hash.info.full_name).to eq('Example User')
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
it 'sets the correct info.country value in the auth hash' do
|
|
384
|
+
expect(auth_hash.info.country).to eq('LV')
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
it 'includes all v009 params in extra.raw_info' do
|
|
388
|
+
expect(auth_hash.extra.raw_info).to include(
|
|
389
|
+
'VK_SERVICE' => '3013',
|
|
390
|
+
'VK_VERSION' => '009',
|
|
391
|
+
'VK_SND_ID' => 'SWEDBANK_LV',
|
|
392
|
+
'VK_USER_NAME' => 'Example User',
|
|
393
|
+
'VK_USER_ID' => '123456-12345',
|
|
394
|
+
'VK_COUNTRY' => 'LV',
|
|
395
|
+
'VK_TOKEN' => '7',
|
|
396
|
+
'VK_RID' => ''
|
|
397
|
+
)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
it 'does not include VK_INFO in extra.raw_info' do
|
|
401
|
+
expect(auth_hash.extra.raw_info).not_to have_key('VK_INFO')
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
context 'with invalid response' do
|
|
406
|
+
it 'detects invalid signature' do
|
|
407
|
+
params = v009_response_params.merge('VK_MAC' => 'invalid signature')
|
|
408
|
+
post '/auth/swedbank/callback', params
|
|
409
|
+
|
|
410
|
+
expect(last_response.status).to eq(302)
|
|
411
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=invalid_response_signature_err&strategy=swedbank')
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
it 'detects unsupported VK_SERVICE values' do
|
|
415
|
+
params = v009_response_params.merge('VK_SERVICE' => '3003')
|
|
416
|
+
post '/auth/swedbank/callback', params
|
|
417
|
+
|
|
418
|
+
expect(last_response.status).to eq(302)
|
|
419
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=unsupported_response_service_err&strategy=swedbank')
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
it 'detects unsupported VK_VERSION values' do
|
|
423
|
+
params = v009_response_params.merge('VK_VERSION' => '008')
|
|
424
|
+
post '/auth/swedbank/callback', params
|
|
425
|
+
|
|
426
|
+
expect(last_response.status).to eq(302)
|
|
427
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=unsupported_response_version_err&strategy=swedbank')
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
it 'detects unsupported VK_ENCODING values' do
|
|
431
|
+
params = v009_response_params.merge('VK_ENCODING' => 'ASCII')
|
|
432
|
+
post '/auth/swedbank/callback', params
|
|
433
|
+
|
|
434
|
+
expect(last_response.status).to eq(302)
|
|
435
|
+
expect(last_response.headers['Location']).to eq('/auth/failure?message=unsupported_response_encoding_err&strategy=swedbank')
|
|
436
|
+
end
|
|
213
437
|
end
|
|
214
438
|
end
|
|
215
439
|
end
|
metadata
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omniauth-swedbank
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mitigate
|
|
8
8
|
- Jānis Kiršteins
|
|
9
9
|
- Kristaps Ērglis
|
|
10
|
-
autorequire:
|
|
11
10
|
bindir: bin
|
|
12
11
|
cert_chain: []
|
|
13
|
-
date:
|
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
14
13
|
dependencies:
|
|
15
14
|
- !ruby/object:Gem::Dependency
|
|
16
15
|
name: omniauth
|
|
@@ -139,6 +138,7 @@ files:
|
|
|
139
138
|
- LICENSE.txt
|
|
140
139
|
- README.md
|
|
141
140
|
- Rakefile
|
|
141
|
+
- docs/migration_008_to_009.md
|
|
142
142
|
- lib/omniauth-swedbank.rb
|
|
143
143
|
- lib/omniauth/locales/omniauth.en.yml
|
|
144
144
|
- lib/omniauth/locales/omniauth.lv.yml
|
|
@@ -148,13 +148,13 @@ files:
|
|
|
148
148
|
- omniauth-swedbank.gemspec
|
|
149
149
|
- spec/certs/request.private.pem
|
|
150
150
|
- spec/certs/response.public.pem
|
|
151
|
+
- spec/certs/response.v009.public.pem
|
|
151
152
|
- spec/omniauth/strategies/swedbank_spec.rb
|
|
152
153
|
- spec/spec_helper.rb
|
|
153
154
|
homepage: https://github.com/mitigate-dev/omniauth-swedbank
|
|
154
155
|
licenses:
|
|
155
156
|
- MIT
|
|
156
157
|
metadata: {}
|
|
157
|
-
post_install_message:
|
|
158
158
|
rdoc_options: []
|
|
159
159
|
require_paths:
|
|
160
160
|
- lib
|
|
@@ -162,19 +162,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
162
162
|
requirements:
|
|
163
163
|
- - ">="
|
|
164
164
|
- !ruby/object:Gem::Version
|
|
165
|
-
version: '
|
|
165
|
+
version: '3.1'
|
|
166
166
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
167
|
requirements:
|
|
168
168
|
- - ">="
|
|
169
169
|
- !ruby/object:Gem::Version
|
|
170
170
|
version: '0'
|
|
171
171
|
requirements: []
|
|
172
|
-
rubygems_version: 3.
|
|
173
|
-
signing_key:
|
|
172
|
+
rubygems_version: 3.6.9
|
|
174
173
|
specification_version: 4
|
|
175
174
|
summary: OmniAuth strategy for Swedbank Banklink
|
|
176
175
|
test_files:
|
|
177
176
|
- spec/certs/request.private.pem
|
|
178
177
|
- spec/certs/response.public.pem
|
|
178
|
+
- spec/certs/response.v009.public.pem
|
|
179
179
|
- spec/omniauth/strategies/swedbank_spec.rb
|
|
180
180
|
- spec/spec_helper.rb
|