omniauth-tinkoff-id 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b125035b7aea156e1dc2532c92789093df39b6e16ddbc66fae2cdc5a1ccc5df7
4
+ data.tar.gz: 01a6b3a46d6b732edfd6566960489f613cc1ca2ee26697db8dc8990aecd1073b
5
+ SHA512:
6
+ metadata.gz: 83002b1b26616bd8d4df706b10bc279016f1fe1dab65dc7f6ccb980a35f4c0ccceca7ab79b94ce029ac0c8e9fd45e725009f95fb0192e49767398a6eebb795bc
7
+ data.tar.gz: 3b059ad6add9f4f628a55bd97cbff1dea21853bb07d17c89bc8e8a687da78d26b9f21d0d056a29ee0e22e93fd5db1062b150c6acdb85209dad311ff7fbab82cb
@@ -0,0 +1,20 @@
1
+ name: Publish Gem
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - v*
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+
11
+ steps:
12
+ - uses: actions/checkout@v3
13
+
14
+ - name: Release Gem
15
+ if: contains(github.ref, 'refs/tags/v')
16
+ uses: cadwallion/publish-rubygems-action@master
17
+ env:
18
+ GITHUB_TOKEN: ${{secrets._GITHUB_TOKEN}}
19
+ RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
20
+ RELEASE_COMMAND: rake release
@@ -0,0 +1,26 @@
1
+ name: Ruby
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+
8
+ pull_request:
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ name: Ruby ${{ matrix.ruby }}
14
+ strategy:
15
+ matrix:
16
+ ruby: ['2.7', '3.0', '3.1', '3.2']
17
+
18
+ steps:
19
+ - uses: actions/checkout@v3
20
+ - name: Set up Ruby
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby }}
24
+ bundler-cache: true
25
+ - name: Run the default task
26
+ run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .DS_Store
13
+ Gemfile.lock
14
+ .idea
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,15 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.7
4
+ SuggestExtensions: false
5
+
6
+ Layout/LineLength:
7
+ Max: 120
8
+
9
+ Metrics/BlockLength:
10
+ Exclude:
11
+ - spec/omniauth/strategies/tinkoff_id_spec.rb
12
+
13
+ Naming/FileName:
14
+ Exclude:
15
+ - lib/omniauth-tinkoff-id.rb
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in omniauth-tinkoff-id.gemspec
6
+ gemspec
7
+
8
+ group :development do
9
+ gem 'rake'
10
+ gem 'rubocop', require: false
11
+ end
12
+
13
+ group :test do
14
+ gem 'rspec'
15
+ gem 'simplecov'
16
+ gem 'webmock'
17
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Yury Druzhkov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # TinkoffId OAuth strategy for OmniAuth
2
+
3
+ [![Ruby](https://github.com/foxford/omniauth-tinkoff-id/actions/workflows/main.yml/badge.svg)](https://github.com/foxford/omniauth-tinkoff-id/actions/workflows/main.yml)
4
+
5
+ [![sponsored by foxford](https://user-images.githubusercontent.com/1637293/224282750-6131144c-fdee-4943-9387-35641cedd99c.svg)](https://foxford.ru?utm_source=github)
6
+
7
+ ## Getting Started
8
+
9
+ ### Prerequisites
10
+
11
+ This gem require [OmniAuth](http://github.com/intridea/omniauth)
12
+
13
+ But you no need add `gem 'omniauth'`.
14
+
15
+ This gem already added.
16
+
17
+ ### Installation
18
+
19
+ gem "omniauth-tinkoff-id"
20
+
21
+ [Join](https://tinkoff.github.io/tinkoff-id/join/) to TinkoffId
22
+
23
+ ### Usage
24
+
25
+ 1. Add to omniauth.rb tinkoff_id provider:
26
+
27
+ Rails.application.config.middleware.use OmniAuth::Builder do
28
+ provider :tinkoff_id, ENV['TINKOFF_CLIENT_ID'], ENV['TINKOFF_CLIENT_SECRET']
29
+ end
30
+
31
+ 2. Add route
32
+
33
+ get '/auth/:provider/callback', to: 'sessions#create'
34
+
35
+ 3. Create SessionController
36
+ ###### note: This controller only as example how to create user by callback
37
+
38
+ class SessionsController < ApplicationController
39
+ def create
40
+ @user = User.find_or_create_from_auth_hash(auth_hash)
41
+ redirect_to '/'
42
+ end
43
+
44
+ protected
45
+
46
+ def auth_hash
47
+ request.env['omniauth.auth']
48
+ end
49
+ end
50
+
51
+
52
+ ## Contributing
53
+
54
+ 1. Fork it (<https://github.com/foxford/omniauth-tinkoff-id/fork>)
55
+ 2. Create your feature branch (git checkout -b my-new-feature)
56
+ 3. Commit your changes (git commit -am 'Add some feature')
57
+ 4. Push to the branch (git push origin my-new-feature)
58
+ 5. Create a new Pull Request
59
+
60
+ ## Versioning
61
+
62
+ We use [SemVer](http://semver.org/) for versioning. For the versions available,
63
+ see the [tags on this repository](ttps://github.com/foxford/omniauth-tinkoff-id/tags).
64
+
65
+ ## Authors
66
+
67
+ * [ Yury Druzhkov ](https://github.com/badlamer) - Initial work
68
+
69
+ See also the list of [contributors](https://github.com/foxford/omniauth-tinkoff-id/contributors) who participated in this project.
70
+
71
+ ## License
72
+
73
+ This project is licensed under the [MIT License](LICENSE.txt).
74
+
75
+ ## Acknowledgments
76
+
77
+ * https://tinkoff.github.io/tinkoff-id/
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'omniauth/strategies/oauth2'
4
+
5
+ module OmniAuth
6
+ module Strategies
7
+ # Authenticate to Tinkoff ID utilizing OAuth 2.0
8
+ # https://tinkoff.github.io/tinkoff-id/
9
+ class TinkoffId < OmniAuth::Strategies::OAuth2
10
+ option :name, 'tinkoff_id'
11
+
12
+ option :client_options, {
13
+ site: 'https://id.tinkoff.ru',
14
+ token_url: '/auth/token',
15
+ authorize_url: '/auth/authorize',
16
+ auth_scheme: :basic_auth
17
+ }
18
+
19
+ uid { raw_info['sub'] }
20
+
21
+ extra do
22
+ prune!({ raw_info: raw_info })
23
+ end
24
+
25
+ info do
26
+ prune!(
27
+ name: raw_info['name'],
28
+ email: verified_email,
29
+ unverified_email: raw_info['email'],
30
+ email_verified: raw_info['email_verified'],
31
+ first_name: raw_info['given_name'],
32
+ last_name: raw_info['family_name'],
33
+ phone_number: verified_phone_number,
34
+ unverified_phone_number: raw_info['phone_number'],
35
+ phone_number_verified: raw_info['phone_number_verified']
36
+ )
37
+ end
38
+
39
+ def callback_url
40
+ options[:redirect_uri] || (full_host + callback_path)
41
+ end
42
+
43
+ private
44
+
45
+ def prune!(hash)
46
+ hash.delete_if do |_, value|
47
+ prune!(value) if value.is_a?(Hash)
48
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
49
+ end
50
+ end
51
+
52
+ def raw_info
53
+ @raw_info ||= connection.post(
54
+ 'userinfo/userinfo', client_id: options[:client_id], client_secret: options[:client_secret]
55
+ ).body
56
+ end
57
+
58
+ def verified_phone_number
59
+ raw_info['phone_number'] if raw_info['phone_number_verified']
60
+ end
61
+
62
+ def verified_email
63
+ raw_info['email'] if raw_info['email_verified']
64
+ end
65
+
66
+ def connection
67
+ @connection ||= Faraday.new('https://id.tinkoff.ru') do |conn|
68
+ conn.request :url_encoded
69
+ conn.request :authorization, 'Bearer', access_token.token
70
+ conn.response :json
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omniauth
4
+ module TinkoffId
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'omniauth/strategies/tinkoff_id'
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'omniauth/tinkoff_id'
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/omniauth/tinkoff_id/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'omniauth-tinkoff-id'
7
+ spec.version = Omniauth::TinkoffId::VERSION
8
+ spec.authors = ['Yury Druzhkov']
9
+ spec.email = ['bad1lamer@gmail.com']
10
+
11
+ spec.summary = 'TinkoffId OAuth2 Strategy for OmniAuth'
12
+ spec.homepage = 'https://github.com/foxford/omniauth-tinkoff-id'
13
+ spec.license = 'MIT'
14
+ spec.required_ruby_version = '>= 2.7.0'
15
+
16
+ spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
17
+ spec.files = `git ls-files`.split("\n")
18
+ spec.require_paths = ['lib']
19
+ spec.add_runtime_dependency 'omniauth-oauth2', '>= 1.5', '<= 1.8.0'
20
+ end
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe OmniAuth::Strategies::TinkoffId do
6
+ subject do
7
+ described_class.new(app, 'client_id', 'client_secret', @options || {}).tap do |strategy|
8
+ allow(strategy).to receive(:request) do
9
+ request
10
+ end
11
+ end
12
+ end
13
+
14
+ let(:request) { double('Request', params: {}, cookies: {}, env: {}, query_string: {}) }
15
+ let(:app) do
16
+ lambda do
17
+ [200, {}, ['Hello.']]
18
+ end
19
+ end
20
+
21
+ before do
22
+ OmniAuth.config.test_mode = true
23
+ end
24
+
25
+ after do
26
+ OmniAuth.config.test_mode = false
27
+ end
28
+
29
+ describe '#client_options' do
30
+ it 'has correct site' do
31
+ expect(subject.client.site).to eq('https://id.tinkoff.ru')
32
+ end
33
+
34
+ it 'has correct authorize_url' do
35
+ expect(subject.client.options[:authorize_url]).to eq('/auth/authorize')
36
+ end
37
+
38
+ it 'has correct token_url' do
39
+ expect(subject.client.options[:token_url]).to eq('/auth/token')
40
+ end
41
+
42
+ describe 'overrides' do
43
+ context 'as strings' do
44
+ it 'allows overriding the site' do
45
+ @options = { client_options: { 'site' => 'https://example.com' } }
46
+ expect(subject.client.site).to eq('https://example.com')
47
+ end
48
+
49
+ it 'allows overriding the authorize_url' do
50
+ @options = { client_options: { 'authorize_url' => 'https://example.com' } }
51
+ expect(subject.client.options[:authorize_url]).to eq('https://example.com')
52
+ end
53
+
54
+ it 'allows overriding the token_url' do
55
+ @options = { client_options: { 'token_url' => 'https://example.com' } }
56
+ expect(subject.client.options[:token_url]).to eq('https://example.com')
57
+ end
58
+ end
59
+
60
+ context 'as symbols' do
61
+ it 'allows overriding the site' do
62
+ @options = { client_options: { site: 'https://example.com' } }
63
+ expect(subject.client.site).to eq('https://example.com')
64
+ end
65
+
66
+ it 'allows overriding the authorize_url' do
67
+ @options = { client_options: { authorize_url: 'https://example.com' } }
68
+ expect(subject.client.options[:authorize_url]).to eq('https://example.com')
69
+ end
70
+
71
+ it 'allows overriding the token_url' do
72
+ @options = { client_options: { token_url: 'https://example.com' } }
73
+ expect(subject.client.options[:token_url]).to eq('https://example.com')
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ describe '#callback_url' do
80
+ let(:base_url) { 'https://example.com' }
81
+
82
+ it 'returns correct default callback path' do
83
+ allow(subject).to receive(:full_host) { base_url }
84
+ allow(subject).to receive(:script_name).and_return('')
85
+ expect(subject.send(:callback_url)).to eq("#{base_url}/auth/tinkoff_id/callback")
86
+ end
87
+
88
+ it 'sets the callback path with script_name if present' do
89
+ allow(subject).to receive(:full_host) { base_url }
90
+ allow(subject).to receive(:script_name).and_return('/v1')
91
+ expect(subject.send(:callback_url)).to eq("#{base_url}/v1/auth/tinkoff_id/callback")
92
+ end
93
+
94
+ it 'sets the callback_path parameter if present' do
95
+ @options = { callback_path: '/auth/foo/callback' }
96
+ allow(subject).to receive(:full_host) { base_url }
97
+ allow(subject).to receive(:script_name).and_return('')
98
+ expect(subject.send(:callback_url)).to eq("#{base_url}/auth/foo/callback")
99
+ end
100
+ end
101
+
102
+ describe '#info' do
103
+ let(:client) { OAuth2::Client.new('abc', 'def') }
104
+ let(:access_token) do
105
+ OAuth2::AccessToken.from_hash(client, access_token: 'valid_access_token',
106
+ token_type: 'Bearer',
107
+ expires_at: 1791,
108
+ refresh_token: 'valid_refresh_token')
109
+ end
110
+
111
+ before do
112
+ allow(subject).to receive(:access_token).and_return(access_token)
113
+ end
114
+
115
+ let(:response_hash) do
116
+ {
117
+ email: 'tinkoff@mail.ru',
118
+ family_name: 'Иванов',
119
+ birthdate: '2000-01-01',
120
+ sub: '923d4812-148c-45v4-a56b-eed15cdd2857',
121
+ name: 'Иванов Олег',
122
+ gender: 'male',
123
+ phone_number: '+79998887766',
124
+ middle_name: 'Юрьевич',
125
+ given_name: 'Олег',
126
+ email_verified: false,
127
+ phone_number_verified: false
128
+ }
129
+ end
130
+
131
+ before do
132
+ stub_request(:post, 'https://id.tinkoff.ru/userinfo/userinfo')
133
+ .with(
134
+ body: { 'client_id' => 'client_id', 'client_secret' => 'client_secret' },
135
+ headers: {
136
+ 'Accept' => '*/*',
137
+ 'Authorization' => "Bearer #{access_token.token}",
138
+ 'Content-Type' => 'application/x-www-form-urlencoded'
139
+ }
140
+ )
141
+ .to_return(status: 200, body: response_hash.to_json, headers: { 'Content-Type': 'application/json' })
142
+ end
143
+
144
+ it 'retunrs info hash' do
145
+ expect(subject.info).to eq(
146
+ name: 'Иванов Олег',
147
+ unverified_email: 'tinkoff@mail.ru',
148
+ email_verified: false,
149
+ first_name: 'Олег',
150
+ last_name: 'Иванов',
151
+ phone_number_verified: false,
152
+ unverified_phone_number: '+79998887766'
153
+ )
154
+ end
155
+
156
+ context 'with verified email' do
157
+ let(:response_hash) do
158
+ { email_verified: true, email: 'tinkoff@mail.ru' }
159
+ end
160
+
161
+ it 'returns info with email' do
162
+ expect(subject.info[:email]).to eq('tinkoff@mail.ru')
163
+ end
164
+ end
165
+
166
+ context 'when verified phone number' do
167
+ let(:response_hash) do
168
+ { phone_number_verified: true, phone_number: '+79998887766' }
169
+ end
170
+
171
+ it 'returns info with email' do
172
+ expect(subject.info[:phone_number]).to eq('+79998887766')
173
+ end
174
+ end
175
+ end
176
+
177
+ describe '#credentials' do
178
+ let(:client) { OAuth2::Client.new('abc', 'def') }
179
+ let(:access_token) do
180
+ OAuth2::AccessToken.from_hash(client, access_token: 'valid_access_token',
181
+ token_type: 'Bearer',
182
+ expires_at: 1791,
183
+ refresh_token: 'valid_refresh_token')
184
+ end
185
+
186
+ before do
187
+ allow(subject).to receive(:access_token).and_return(access_token)
188
+ subject.options.client_options[:connection_build] = proc do |builder|
189
+ builder.request :url_encoded
190
+ builder.request :basic_auth, :basic, subject.client.options[:client_id], subject.client.options[:client_secrect]
191
+ builder.adapter :test do |stub|
192
+ stub.post('/auth/token') do
193
+ [200, { 'Content-Type' => 'application/json; charset=UTF-8' }, JSON.dump(
194
+ sub: '2xxxxxxc-8xx6-4xxd-9xx5-bxxxxxxxxxxa'
195
+ )]
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ it 'returns access token and (optionally) refresh token' do
202
+ expect(subject.credentials.to_h)
203
+ .to match(hash_including(
204
+ {
205
+ 'expires' => true,
206
+ 'expires_at' => 1791,
207
+ 'refresh_token' => 'valid_refresh_token',
208
+ 'token' => 'valid_access_token'
209
+ }
210
+ ))
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec'
4
+ require 'webmock/rspec'
5
+ require 'omniauth'
6
+ require 'omniauth/strategies/tinkoff_id'
7
+
8
+ RSpec.configure do |config|
9
+ config.include WebMock::API
10
+ config.extend OmniAuth::Test::StrategyMacros, type: :strategy
11
+ config.expect_with :rspec do |c|
12
+ c.syntax = :expect
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-tinkoff-id
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yury Druzhkov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-03-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: omniauth-oauth2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ - - "<="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.8.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.5'
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.8.0
33
+ description:
34
+ email:
35
+ - bad1lamer@gmail.com
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - ".github/workflows/gem-push.yml"
41
+ - ".github/workflows/main.yml"
42
+ - ".gitignore"
43
+ - ".rspec"
44
+ - ".rubocop.yml"
45
+ - Gemfile
46
+ - LICENSE.txt
47
+ - README.md
48
+ - Rakefile
49
+ - lib/omniauth-tinkoff-id.rb
50
+ - lib/omniauth/strategies/tinkoff_id.rb
51
+ - lib/omniauth/tinkoff_id.rb
52
+ - lib/omniauth/tinkoff_id/version.rb
53
+ - omniauth-tinkoff-id.gemspec
54
+ - spec/omniauth/strategies/tinkoff_id_spec.rb
55
+ - spec/spec_helper.rb
56
+ homepage: https://github.com/foxford/omniauth-tinkoff-id
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.7.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 3.4.6
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: TinkoffId OAuth2 Strategy for OmniAuth
79
+ test_files: []