omniauth-clover-oauth2 1.0.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: 718583b3f5599a91cb7c93b32f01af9ac8a658e3e351b4c601b9e5319a1345a3
4
+ data.tar.gz: 152836810576c0efa3c17d4da5c56bfe6cda667f9a25c40e80fddd2ef1235081
5
+ SHA512:
6
+ metadata.gz: 536d7e5a16bc73d381686f44ea96eeac9bf1225d6830a1a42aab054aeaf49552fcb48790cccb7473d4eed00f33b9521a71d9aab2906aed10a98a747c8b29d78e
7
+ data.tar.gz: a6f5190005c3f2ec1e1cdec91991728bd2b3ba11fda0f67e121bf05016ff10703d00e0e1e14aef83fd2f704c80e09ed3abef7dfb0d4d9c8cb29d8f112a80313b
data/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2024-01-30
9
+
10
+ ### Added
11
+
12
+ - Initial release of omniauth-clover-oauth2
13
+ - OmniAuth OAuth2 strategy for Clover POS
14
+ - Support for sandbox and production environments
15
+ - Automatic endpoint switching based on environment
16
+ - Merchant and employee info fetching
17
+ - Comprehensive RSpec test suite
18
+ - GitHub Actions CI/CD workflow
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 dan1d
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,258 @@
1
+ # OmniAuth Clover OAuth2 Strategy
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/omniauth-clover-oauth2.svg)](https://badge.fury.io/rb/omniauth-clover-oauth2)
4
+ [![CI](https://github.com/dan1d/omniauth-clover-oauth2/actions/workflows/ci.yml/badge.svg)](https://github.com/dan1d/omniauth-clover-oauth2/actions/workflows/ci.yml)
5
+
6
+ An OmniAuth strategy for authenticating with [Clover POS](https://www.clover.com/) using OAuth 2.0.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'omniauth-clover-oauth2'
14
+ ```
15
+
16
+ Then execute:
17
+
18
+ ```bash
19
+ $ bundle install
20
+ ```
21
+
22
+ Or install it yourself:
23
+
24
+ ```bash
25
+ $ gem install omniauth-clover-oauth2
26
+ ```
27
+
28
+ ## Clover Developer Setup
29
+
30
+ 1. Go to the [Clover Developer Dashboard](https://sandbox.dev.clover.com/developer-home/create-account)
31
+ 2. Create a new app or select an existing one
32
+ 3. Navigate to **App Settings** > **REST Configuration**
33
+ 4. Note your **App ID** (Client ID) and **App Secret** (Client Secret)
34
+ 5. Configure your **Site URL** and **Callback URL** (e.g., `https://yourapp.com/auth/clover_oauth2/callback`)
35
+
36
+ For more details, read the [Clover OAuth 2.0 documentation](https://docs.clover.com/docs/oauth-flows-in-clover).
37
+
38
+ ## Usage
39
+
40
+ ### Standalone OmniAuth
41
+
42
+ Add the middleware to your application in `config/initializers/omniauth.rb`:
43
+
44
+ ```ruby
45
+ Rails.application.config.middleware.use OmniAuth::Builder do
46
+ provider :clover_oauth2,
47
+ ENV['CLOVER_CLIENT_ID'],
48
+ ENV['CLOVER_CLIENT_SECRET'],
49
+ sandbox: Rails.env.development?
50
+ end
51
+
52
+ # Required for OmniAuth 2.0+
53
+ OmniAuth.config.allowed_request_methods = %i[get post]
54
+ ```
55
+
56
+ You can now access the OmniAuth Clover OAuth2 URL at `/auth/clover_oauth2`.
57
+
58
+ ### With Devise
59
+
60
+ Add the provider to your Devise configuration in `config/initializers/devise.rb`:
61
+
62
+ ```ruby
63
+ config.omniauth :clover_oauth2,
64
+ ENV['CLOVER_CLIENT_ID'],
65
+ ENV['CLOVER_CLIENT_SECRET'],
66
+ sandbox: !Rails.env.production?
67
+ ```
68
+
69
+ **Do not** create a separate `config/initializers/omniauth.rb` file when using Devise, as it will conflict.
70
+
71
+ Add to your routes in `config/routes.rb`:
72
+
73
+ ```ruby
74
+ devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
75
+ ```
76
+
77
+ Make your User model omniauthable in `app/models/user.rb`:
78
+
79
+ ```ruby
80
+ devise :omniauthable, omniauth_providers: [:clover_oauth2]
81
+ ```
82
+
83
+ Create the callbacks controller at `app/controllers/users/omniauth_callbacks_controller.rb`:
84
+
85
+ ```ruby
86
+ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
87
+ def clover_oauth2
88
+ @user = User.from_omniauth(request.env['omniauth.auth'])
89
+
90
+ if @user.persisted?
91
+ flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: 'Clover')
92
+ sign_in_and_redirect @user, event: :authentication
93
+ else
94
+ session['devise.clover_data'] = request.env['omniauth.auth'].except('extra')
95
+ redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
96
+ end
97
+ end
98
+
99
+ def failure
100
+ redirect_to root_path, alert: "Authentication failed: #{failure_message}"
101
+ end
102
+ end
103
+ ```
104
+
105
+ Add the `from_omniauth` method to your User model:
106
+
107
+ ```ruby
108
+ def self.from_omniauth(auth)
109
+ where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
110
+ user.email = auth.info.email
111
+ user.password = Devise.friendly_token[0, 20]
112
+ user.name = auth.info.name
113
+ # Add any other fields you need
114
+ end
115
+ end
116
+ ```
117
+
118
+ For your views, create a login link:
119
+
120
+ ```erb
121
+ <%= link_to "Sign in with Clover", user_clover_oauth2_omniauth_authorize_path, method: :post %>
122
+ ```
123
+
124
+ ## Configuration Options
125
+
126
+ | Option | Default | Description |
127
+ |--------|---------|-------------|
128
+ | `sandbox` | `true` | Use sandbox environment. Set to `false` for production. |
129
+ | `provider_ignores_state` | `true` | Clover doesn't properly return the OAuth state parameter. |
130
+
131
+ ### Example with all options:
132
+
133
+ ```ruby
134
+ provider :clover_oauth2,
135
+ ENV['CLOVER_CLIENT_ID'],
136
+ ENV['CLOVER_CLIENT_SECRET'],
137
+ sandbox: false # Use production environment
138
+ ```
139
+
140
+ ## Auth Hash
141
+
142
+ Here's an example of the authentication hash available in the callback by accessing `request.env['omniauth.auth']`:
143
+
144
+ ```ruby
145
+ {
146
+ "provider" => "clover_oauth2",
147
+ "uid" => "ABCD1234567890",
148
+ "info" => {
149
+ "email" => "employee@example.com",
150
+ "first_name" => "John",
151
+ "last_name" => "Doe",
152
+ "name" => "John Doe",
153
+ "merchant_id" => "ABCD1234567890",
154
+ "employee_id" => "EFGH0987654321"
155
+ },
156
+ "credentials" => {
157
+ "token" => "ACCESS_TOKEN",
158
+ "refresh_token" => "REFRESH_TOKEN",
159
+ "expires_at" => 1704067200,
160
+ "expires" => true
161
+ },
162
+ "extra" => {
163
+ "merchant_id" => "ABCD1234567890",
164
+ "employee_id" => "EFGH0987654321",
165
+ "business_name" => "Acme Restaurant",
166
+ "raw_info" => {
167
+ "merchant" => {
168
+ "id" => "ABCD1234567890",
169
+ "name" => "Acme Restaurant",
170
+ "address" => { ... }
171
+ },
172
+ "employee" => {
173
+ "id" => "EFGH0987654321",
174
+ "name" => "John Doe",
175
+ "email" => "employee@example.com",
176
+ "role" => "ADMIN"
177
+ }
178
+ }
179
+ }
180
+ }
181
+ ```
182
+
183
+ ## Sandbox vs Production
184
+
185
+ Clover uses different URLs for sandbox and production environments:
186
+
187
+ | Environment | Authorize URL | API URL |
188
+ |-------------|---------------|---------|
189
+ | Sandbox | `https://sandbox.dev.clover.com` | `https://apisandbox.dev.clover.com` |
190
+ | Production | `https://www.clover.com` | `https://api.clover.com` |
191
+
192
+ The gem automatically handles this based on the `sandbox` option.
193
+
194
+ **Important:** You need separate Clover apps for sandbox and production. Make sure to use the correct credentials for each environment.
195
+
196
+ ## Clover OAuth2 Specifics
197
+
198
+ This gem handles several Clover-specific OAuth2 behaviors:
199
+
200
+ 1. **JSON Token Exchange**: Clover requires `Content-Type: application/json` for the token exchange endpoint (not the standard `application/x-www-form-urlencoded`).
201
+
202
+ 2. **Split Endpoints**: Clover uses different domains for authorization (`www.clover.com`) and token/API endpoints (`api.clover.com`).
203
+
204
+ 3. **State Parameter**: Clover doesn't properly return the OAuth state parameter, so this gem sets `provider_ignores_state` to `true` by default.
205
+
206
+ 4. **Merchant/Employee Context**: The OAuth callback includes `merchant_id` and `employee_id` query parameters, which are used to fetch additional user information.
207
+
208
+ ## Storing Tokens
209
+
210
+ You'll likely want to store the access token and refresh token to make API calls later:
211
+
212
+ ```ruby
213
+ def self.from_omniauth(auth)
214
+ user = where(provider: auth.provider, uid: auth.uid).first_or_create do |u|
215
+ u.email = auth.info.email
216
+ u.password = Devise.friendly_token[0, 20]
217
+ end
218
+
219
+ # Update tokens on each login
220
+ user.update(
221
+ clover_access_token: auth.credentials.token,
222
+ clover_refresh_token: auth.credentials.refresh_token,
223
+ clover_token_expires_at: Time.at(auth.credentials.expires_at),
224
+ clover_merchant_id: auth.info.merchant_id,
225
+ clover_employee_id: auth.info.employee_id
226
+ )
227
+
228
+ user
229
+ end
230
+ ```
231
+
232
+ ## Token Refresh
233
+
234
+ Clover access tokens expire (typically after 1 year, but check your app settings). To refresh tokens, you'll need to implement token refresh logic using the Clover API directly.
235
+
236
+ ## Contributing
237
+
238
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dan1d/omniauth-clover-oauth2.
239
+
240
+ 1. Fork it
241
+ 2. Create your feature branch (`git checkout -b feature/my-new-feature`)
242
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
243
+ 4. Push to the branch (`git push origin feature/my-new-feature`)
244
+ 5. Create a new Pull Request
245
+
246
+ ## Development
247
+
248
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rspec` to run the tests.
249
+
250
+ ```bash
251
+ bundle install
252
+ bundle exec rspec
253
+ bundle exec rubocop
254
+ ```
255
+
256
+ ## License
257
+
258
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAuth
4
+ module CloverOauth2
5
+ VERSION = '1.0.0'
6
+ end
7
+ end
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'omniauth-oauth2'
4
+ require 'faraday'
5
+ require 'json'
6
+ require 'ostruct'
7
+
8
+ module OmniAuth
9
+ module Strategies
10
+ # OmniAuth strategy for Clover POS OAuth 2.0
11
+ #
12
+ # Clover OAuth2 has several non-standard behaviors:
13
+ # - Requires JSON Content-Type for token exchange (not form-urlencoded)
14
+ # - Uses different domains for authorize vs token/API endpoints
15
+ # - Doesn't properly return the state parameter
16
+ # - Includes merchant_id and employee_id in callback params
17
+ #
18
+ # @example Basic usage
19
+ # provider :clover_oauth2, ENV['CLOVER_CLIENT_ID'], ENV['CLOVER_CLIENT_SECRET']
20
+ #
21
+ # @example With sandbox disabled (production)
22
+ # provider :clover_oauth2, ENV['CLOVER_CLIENT_ID'], ENV['CLOVER_CLIENT_SECRET'],
23
+ # sandbox: false
24
+ #
25
+ class CloverOauth2 < OmniAuth::Strategies::OAuth2
26
+ option :name, 'clover_oauth2'
27
+
28
+ # Sandbox mode is enabled by default for safety
29
+ option :sandbox, true
30
+
31
+ # Clover doesn't properly return the OAuth state parameter
32
+ option :provider_ignores_state, true
33
+
34
+ # Default client options - these get merged with environment-specific URLs
35
+ option :client_options, {
36
+ authorize_url: '/oauth/v2/authorize'
37
+ }
38
+
39
+ # UID is the merchant_id from Clover
40
+ uid { merchant_id }
41
+
42
+ info do
43
+ {
44
+ email: employee_info['email'],
45
+ first_name: extract_first_name,
46
+ last_name: extract_last_name,
47
+ name: employee_info['name'] || merchant_info['name'],
48
+ merchant_id: merchant_id,
49
+ employee_id: employee_id
50
+ }
51
+ end
52
+
53
+ extra do
54
+ {
55
+ merchant_id: merchant_id,
56
+ employee_id: employee_id,
57
+ business_name: merchant_info['name'],
58
+ raw_info: {
59
+ merchant: merchant_info,
60
+ employee: employee_info
61
+ }
62
+ }
63
+ end
64
+
65
+ credentials do
66
+ hash = { 'token' => access_token.token }
67
+ hash['refresh_token'] = access_token.refresh_token if access_token.refresh_token
68
+ hash['expires_at'] = access_token.expires_at if access_token.expires_at
69
+ hash['expires'] = access_token.expires?
70
+ hash
71
+ end
72
+
73
+ # Override the OAuth2 client to use the correct site URL based on sandbox mode
74
+ def client
75
+ ::OAuth2::Client.new(
76
+ options.client_id,
77
+ options.client_secret,
78
+ deep_symbolize(options.client_options.merge(site: authorize_site_url))
79
+ )
80
+ end
81
+
82
+ # Merchant info fetched from Clover API
83
+ def merchant_info
84
+ @merchant_info ||= fetch_merchant_info
85
+ end
86
+
87
+ # Employee info fetched from Clover API
88
+ def employee_info
89
+ @employee_info ||= fetch_employee_info
90
+ end
91
+
92
+ # Merchant ID from callback params
93
+ def merchant_id
94
+ request.params['merchant_id']
95
+ end
96
+
97
+ # Employee ID from callback params
98
+ def employee_id
99
+ request.params['employee_id']
100
+ end
101
+
102
+ private
103
+
104
+ # Authorization site URL (where users are redirected to authorize)
105
+ def authorize_site_url
106
+ if sandbox?
107
+ 'https://sandbox.dev.clover.com'
108
+ else
109
+ 'https://www.clover.com'
110
+ end
111
+ end
112
+
113
+ # API base URL (where token exchange and API calls happen)
114
+ def api_base_url
115
+ if sandbox?
116
+ 'https://apisandbox.dev.clover.com'
117
+ else
118
+ 'https://api.clover.com'
119
+ end
120
+ end
121
+
122
+ def sandbox?
123
+ options.sandbox
124
+ end
125
+
126
+ def extract_first_name
127
+ employee_info['name']&.split&.first
128
+ end
129
+
130
+ def extract_last_name
131
+ name = employee_info['name']
132
+ return nil unless name
133
+
134
+ parts = name.split
135
+ parts.drop(1).join(' ') if parts.length > 1
136
+ end
137
+
138
+ def fetch_merchant_info
139
+ return {} unless merchant_id && access_token
140
+
141
+ api_url = "#{api_base_url}/v3/merchants/#{merchant_id}"
142
+ response = make_api_request(api_url)
143
+
144
+ return {} unless response&.success?
145
+
146
+ JSON.parse(response.body)
147
+ rescue StandardError => e
148
+ log(:error, "Failed to fetch merchant info: #{e.message}")
149
+ {}
150
+ end
151
+
152
+ def fetch_employee_info
153
+ return {} unless merchant_id && employee_id && access_token
154
+
155
+ api_url = "#{api_base_url}/v3/merchants/#{merchant_id}/employees/#{employee_id}"
156
+ response = make_api_request(api_url)
157
+
158
+ return {} unless response&.success?
159
+
160
+ JSON.parse(response.body)
161
+ rescue StandardError => e
162
+ log(:error, "Failed to fetch employee info: #{e.message}")
163
+ {}
164
+ end
165
+
166
+ def make_api_request(url)
167
+ Faraday.get(url) do |req|
168
+ req.headers['Authorization'] = "Bearer #{access_token.token}"
169
+ req.headers['Accept'] = 'application/json'
170
+ end
171
+ end
172
+
173
+ # Override to handle Clover's non-standard token endpoint
174
+ # Clover requires JSON body for token exchange, not form-urlencoded
175
+ def build_access_token
176
+ code = request.params['code']
177
+ token_url = "#{api_base_url}/oauth/v2/token"
178
+
179
+ # Clover requires JSON body for token exchange
180
+ response = Faraday.post(token_url) do |req|
181
+ req.headers['Content-Type'] = 'application/json'
182
+ req.headers['Accept'] = 'application/json'
183
+ req.body = {
184
+ client_id: options.client_id,
185
+ client_secret: options.client_secret,
186
+ code: code
187
+ }.to_json
188
+ end
189
+
190
+ unless response.success?
191
+ error_body = begin
192
+ JSON.parse(response.body)
193
+ rescue JSON::ParserError
194
+ { 'error' => response.body }
195
+ end
196
+ raise ::OAuth2::Error, build_error_response(error_body, response)
197
+ end
198
+
199
+ token_data = JSON.parse(response.body)
200
+
201
+ # Build OAuth2 access token from response
202
+ ::OAuth2::AccessToken.new(
203
+ client,
204
+ token_data['access_token'],
205
+ refresh_token: token_data['refresh_token'],
206
+ expires_in: token_data['access_token_expiration'],
207
+ token_type: 'Bearer'
208
+ )
209
+ end
210
+
211
+ def build_error_response(error_body, response)
212
+ OpenStruct.new(
213
+ parsed: error_body,
214
+ body: response.body,
215
+ status: response.status
216
+ )
217
+ end
218
+
219
+ def callback_url
220
+ full_host + callback_path
221
+ end
222
+
223
+ def log(level, message)
224
+ return unless defined?(OmniAuth.logger) && OmniAuth.logger
225
+
226
+ OmniAuth.logger.send(level, "[CloverOauth2] #{message}")
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'omniauth-oauth2'
4
+ require 'omniauth/clover_oauth2/version'
5
+ require 'omniauth/strategies/clover_oauth2'
metadata ADDED
@@ -0,0 +1,211 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-clover-oauth2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - dan1d
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: faraday
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.0'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '1.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '3.0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: omniauth-oauth2
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '1.8'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '1.8'
46
+ - !ruby/object:Gem::Dependency
47
+ name: ostruct
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '0.6'
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '0.6'
60
+ - !ruby/object:Gem::Dependency
61
+ name: bundler
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '2.0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '2.0'
74
+ - !ruby/object:Gem::Dependency
75
+ name: rack-test
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '2.1'
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '2.1'
88
+ - !ruby/object:Gem::Dependency
89
+ name: rake
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '13.0'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '13.0'
102
+ - !ruby/object:Gem::Dependency
103
+ name: rspec
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '3.12'
109
+ type: :development
110
+ prerelease: false
111
+ version_requirements: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '3.12'
116
+ - !ruby/object:Gem::Dependency
117
+ name: rubocop
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '1.50'
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '1.50'
130
+ - !ruby/object:Gem::Dependency
131
+ name: rubocop-rspec
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '2.20'
137
+ type: :development
138
+ prerelease: false
139
+ version_requirements: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '2.20'
144
+ - !ruby/object:Gem::Dependency
145
+ name: simplecov
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '0.22'
151
+ type: :development
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '0.22'
158
+ - !ruby/object:Gem::Dependency
159
+ name: webmock
160
+ requirement: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '3.18'
165
+ type: :development
166
+ prerelease: false
167
+ version_requirements: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - "~>"
170
+ - !ruby/object:Gem::Version
171
+ version: '3.18'
172
+ description: An OmniAuth strategy for authenticating with Clover POS using OAuth 2.0.
173
+ Supports both sandbox and production environments with automatic endpoint switching.
174
+ email:
175
+ - dan1d@users.noreply.github.com
176
+ executables: []
177
+ extensions: []
178
+ extra_rdoc_files: []
179
+ files:
180
+ - CHANGELOG.md
181
+ - LICENSE.txt
182
+ - README.md
183
+ - lib/omniauth-clover-oauth2.rb
184
+ - lib/omniauth/clover_oauth2/version.rb
185
+ - lib/omniauth/strategies/clover_oauth2.rb
186
+ homepage: https://github.com/dan1d/omniauth-clover-oauth2
187
+ licenses:
188
+ - MIT
189
+ metadata:
190
+ homepage_uri: https://github.com/dan1d/omniauth-clover-oauth2
191
+ source_code_uri: https://github.com/dan1d/omniauth-clover-oauth2
192
+ changelog_uri: https://github.com/dan1d/omniauth-clover-oauth2/blob/main/CHANGELOG.md
193
+ rubygems_mfa_required: 'true'
194
+ rdoc_options: []
195
+ require_paths:
196
+ - lib
197
+ required_ruby_version: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: 3.0.0
202
+ required_rubygems_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ requirements: []
208
+ rubygems_version: 3.6.9
209
+ specification_version: 4
210
+ summary: OmniAuth OAuth2 strategy for Clover POS
211
+ test_files: []