omniauth-nft 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: fab767120631dccbb9a10a5bd2e6b4dd278d752dfd23d8d982c6901b3e0ffc1c
4
+ data.tar.gz: aae937ccbc46271453178650d8ef148755ca02b10fa93e4ddbeacfc3632cf97c
5
+ SHA512:
6
+ metadata.gz: da8b9ae41177ecb698515d0f814bf4f869e176e847120bb87802ac54ce4a9e78e1f2d2ae450749e30cb00137eecab1883cd06533e375b30e510b808aa298346a
7
+ data.tar.gz: 6bf3ae6e0193d14fbb06aef22f4e3b490bf0d2d82eadd03de7767c6744bccc4e7979ed6f1982a723049ff9b86b3a44dd6905fe05f3aca3871f066bdf634f315b
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ NewCops: enable
4
+
5
+ Style/StringLiterals:
6
+ Enabled: true
7
+ EnforcedStyle: double_quotes
8
+
9
+ Style/StringLiteralsInInterpolation:
10
+ Enabled: true
11
+ EnforcedStyle: double_quotes
12
+
13
+ Layout/LineLength:
14
+ Max: 120
15
+
16
+ Metrics/BlockLength:
17
+ Exclude:
18
+ - '*.gemspec'
19
+ - 'spec/**/*'
20
+
21
+ Naming/FileName:
22
+ Exclude:
23
+ - 'lib/omniauth-nft.rb'
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.3
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-12-31
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in omniauth-nft.gemspec
6
+ gemspec
7
+
8
+ gem "eth", "~> 0.4.17"
9
+ gem "rails", "~> 7.0"
10
+ gem "rake", "~> 13.0"
11
+ gem "rspec", "~> 3.0"
12
+ gem "rubocop", "~> 1.21"
13
+ gem "rubocop-rails", "~> 2.13"
14
+ gem "rubocop-rake", "~> 0.6.0"
15
+ gem "rubocop-rspec", "~> 2.7"
data/Gemfile.lock ADDED
@@ -0,0 +1,221 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ omniauth-nft (0.1.0)
5
+ eth (~> 0.4)
6
+ nft_checker (~> 0.3)
7
+ omniauth (~> 2.0)
8
+ rails (>= 6.0.0)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ actioncable (7.0.0)
14
+ actionpack (= 7.0.0)
15
+ activesupport (= 7.0.0)
16
+ nio4r (~> 2.0)
17
+ websocket-driver (>= 0.6.1)
18
+ actionmailbox (7.0.0)
19
+ actionpack (= 7.0.0)
20
+ activejob (= 7.0.0)
21
+ activerecord (= 7.0.0)
22
+ activestorage (= 7.0.0)
23
+ activesupport (= 7.0.0)
24
+ mail (>= 2.7.1)
25
+ actionmailer (7.0.0)
26
+ actionpack (= 7.0.0)
27
+ actionview (= 7.0.0)
28
+ activejob (= 7.0.0)
29
+ activesupport (= 7.0.0)
30
+ mail (~> 2.5, >= 2.5.4)
31
+ rails-dom-testing (~> 2.0)
32
+ actionpack (7.0.0)
33
+ actionview (= 7.0.0)
34
+ activesupport (= 7.0.0)
35
+ rack (~> 2.0, >= 2.2.0)
36
+ rack-test (>= 0.6.3)
37
+ rails-dom-testing (~> 2.0)
38
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
39
+ actiontext (7.0.0)
40
+ actionpack (= 7.0.0)
41
+ activerecord (= 7.0.0)
42
+ activestorage (= 7.0.0)
43
+ activesupport (= 7.0.0)
44
+ globalid (>= 0.6.0)
45
+ nokogiri (>= 1.8.5)
46
+ actionview (7.0.0)
47
+ activesupport (= 7.0.0)
48
+ builder (~> 3.1)
49
+ erubi (~> 1.4)
50
+ rails-dom-testing (~> 2.0)
51
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
52
+ activejob (7.0.0)
53
+ activesupport (= 7.0.0)
54
+ globalid (>= 0.3.6)
55
+ activemodel (7.0.0)
56
+ activesupport (= 7.0.0)
57
+ activerecord (7.0.0)
58
+ activemodel (= 7.0.0)
59
+ activesupport (= 7.0.0)
60
+ activestorage (7.0.0)
61
+ actionpack (= 7.0.0)
62
+ activejob (= 7.0.0)
63
+ activerecord (= 7.0.0)
64
+ activesupport (= 7.0.0)
65
+ marcel (~> 1.0)
66
+ mini_mime (>= 1.1.0)
67
+ activesupport (7.0.0)
68
+ concurrent-ruby (~> 1.0, >= 1.0.2)
69
+ i18n (>= 1.6, < 2)
70
+ minitest (>= 5.1)
71
+ tzinfo (~> 2.0)
72
+ ast (2.4.2)
73
+ builder (3.2.4)
74
+ concurrent-ruby (1.1.9)
75
+ crass (1.0.6)
76
+ diff-lcs (1.5.0)
77
+ erubi (1.10.0)
78
+ eth (0.4.17)
79
+ ffi (~> 1.15)
80
+ keccak (~> 1.3)
81
+ money-tree (~> 0.10)
82
+ rlp (~> 0.7)
83
+ scrypt (~> 3.0)
84
+ ffi (1.15.4)
85
+ ffi-compiler (1.0.1)
86
+ ffi (>= 1.0.0)
87
+ rake
88
+ globalid (1.0.0)
89
+ activesupport (>= 5.0)
90
+ hashie (5.0.0)
91
+ httparty (0.20.0)
92
+ mime-types (~> 3.0)
93
+ multi_xml (>= 0.5.2)
94
+ i18n (1.8.11)
95
+ concurrent-ruby (~> 1.0)
96
+ keccak (1.3.0)
97
+ loofah (2.13.0)
98
+ crass (~> 1.0.2)
99
+ nokogiri (>= 1.5.9)
100
+ mail (2.7.1)
101
+ mini_mime (>= 0.1.1)
102
+ marcel (1.0.2)
103
+ method_source (1.0.0)
104
+ mime-types (3.4.1)
105
+ mime-types-data (~> 3.2015)
106
+ mime-types-data (3.2021.1115)
107
+ mini_mime (1.1.2)
108
+ minitest (5.15.0)
109
+ money-tree (0.10.0)
110
+ ffi
111
+ multi_xml (0.6.0)
112
+ nft_checker (0.3.0)
113
+ httparty (~> 0.20)
114
+ nio4r (2.5.8)
115
+ nokogiri (1.12.5-x86_64-darwin)
116
+ racc (~> 1.4)
117
+ omniauth (2.0.4)
118
+ hashie (>= 3.4.6)
119
+ rack (>= 1.6.2, < 3)
120
+ rack-protection
121
+ parallel (1.21.0)
122
+ parser (3.0.3.2)
123
+ ast (~> 2.4.1)
124
+ racc (1.6.0)
125
+ rack (2.2.3)
126
+ rack-protection (2.1.0)
127
+ rack
128
+ rack-test (1.1.0)
129
+ rack (>= 1.0, < 3)
130
+ rails (7.0.0)
131
+ actioncable (= 7.0.0)
132
+ actionmailbox (= 7.0.0)
133
+ actionmailer (= 7.0.0)
134
+ actionpack (= 7.0.0)
135
+ actiontext (= 7.0.0)
136
+ actionview (= 7.0.0)
137
+ activejob (= 7.0.0)
138
+ activemodel (= 7.0.0)
139
+ activerecord (= 7.0.0)
140
+ activestorage (= 7.0.0)
141
+ activesupport (= 7.0.0)
142
+ bundler (>= 1.15.0)
143
+ railties (= 7.0.0)
144
+ rails-dom-testing (2.0.3)
145
+ activesupport (>= 4.2.0)
146
+ nokogiri (>= 1.6)
147
+ rails-html-sanitizer (1.4.2)
148
+ loofah (~> 2.3)
149
+ railties (7.0.0)
150
+ actionpack (= 7.0.0)
151
+ activesupport (= 7.0.0)
152
+ method_source
153
+ rake (>= 12.2)
154
+ thor (~> 1.0)
155
+ zeitwerk (~> 2.5)
156
+ rainbow (3.0.0)
157
+ rake (13.0.6)
158
+ regexp_parser (2.2.0)
159
+ rexml (3.2.5)
160
+ rlp (0.7.3)
161
+ rspec (3.10.0)
162
+ rspec-core (~> 3.10.0)
163
+ rspec-expectations (~> 3.10.0)
164
+ rspec-mocks (~> 3.10.0)
165
+ rspec-core (3.10.1)
166
+ rspec-support (~> 3.10.0)
167
+ rspec-expectations (3.10.1)
168
+ diff-lcs (>= 1.2.0, < 2.0)
169
+ rspec-support (~> 3.10.0)
170
+ rspec-mocks (3.10.2)
171
+ diff-lcs (>= 1.2.0, < 2.0)
172
+ rspec-support (~> 3.10.0)
173
+ rspec-support (3.10.3)
174
+ rubocop (1.24.1)
175
+ parallel (~> 1.10)
176
+ parser (>= 3.0.0.0)
177
+ rainbow (>= 2.2.2, < 4.0)
178
+ regexp_parser (>= 1.8, < 3.0)
179
+ rexml
180
+ rubocop-ast (>= 1.15.1, < 2.0)
181
+ ruby-progressbar (~> 1.7)
182
+ unicode-display_width (>= 1.4.0, < 3.0)
183
+ rubocop-ast (1.15.1)
184
+ parser (>= 3.0.1.1)
185
+ rubocop-rails (2.13.0)
186
+ activesupport (>= 4.2.0)
187
+ rack (>= 1.1)
188
+ rubocop (>= 1.7.0, < 2.0)
189
+ rubocop-rake (0.6.0)
190
+ rubocop (~> 1.0)
191
+ rubocop-rspec (2.7.0)
192
+ rubocop (~> 1.19)
193
+ ruby-progressbar (1.11.0)
194
+ scrypt (3.0.7)
195
+ ffi-compiler (>= 1.0, < 2.0)
196
+ thor (1.1.0)
197
+ tzinfo (2.0.4)
198
+ concurrent-ruby (~> 1.0)
199
+ unicode-display_width (2.1.0)
200
+ websocket-driver (0.7.5)
201
+ websocket-extensions (>= 0.1.0)
202
+ websocket-extensions (0.1.5)
203
+ zeitwerk (2.5.3)
204
+
205
+ PLATFORMS
206
+ x86_64-darwin-19
207
+ x86_64-darwin-21
208
+
209
+ DEPENDENCIES
210
+ eth (~> 0.4.17)
211
+ omniauth-nft!
212
+ rails (~> 7.0)
213
+ rake (~> 13.0)
214
+ rspec (~> 3.0)
215
+ rubocop (~> 1.21)
216
+ rubocop-rails (~> 2.13)
217
+ rubocop-rake (~> 0.6.0)
218
+ rubocop-rspec (~> 2.7)
219
+
220
+ BUNDLED WITH
221
+ 2.2.32
data/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # OmniAuth::Nft
2
+
3
+ OmniAuth strategy for authenticating via NFT ownership
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'omniauth-nft'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install omniauth-nft
20
+
21
+ ## Usage
22
+
23
+ Add as a strategy to your omniauth config. Eg:
24
+
25
+ ```ruby
26
+ Rails.application.config.middleware.use OmniAuth::Builder do
27
+ provider :nft,
28
+ checker_type: :opensea,
29
+ checker_options: { testnet: true },
30
+ nft_collection: { slug: 'untitled-collection-4919696' }
31
+ provider :developer unless Rails.env.production?
32
+ end
33
+ ```
34
+ This configuration will run against OpenSea testnet and allow login for anyone that owns an NFT in the
35
+ [Test Prota Collection](https://testnets.opensea.io/collection/untitled-collection-4919696)
36
+
37
+ UID is set to a combination of nft_collection slug and the nft token_id.
38
+ Authenticated users will have an auth.info hash with the name and image url of the NFT.
39
+ `auth.extra` contains a `wallet` key (the authenticated wallet address) and a `raw_info`
40
+ hash which contains all NFT metadata retrieved by `NftChecker`.
41
+
42
+ You can implement `User#find_or_create_from_auth_hash` like this:
43
+ ```ruby
44
+ def User.find_or_create_from_auth_hash(auth)
45
+ identity = { provider: auth['provider'], uid: auth['uid'] }
46
+ User.find_or_create_by!(identity) do |record|
47
+ case auth[:provider]
48
+ when 'nft'
49
+ record.name = auth.info['name']
50
+ record.profile_url = auth.info['image']
51
+ else
52
+ raise 'Unexpected provider!'
53
+ end
54
+ end
55
+ end
56
+ ```
57
+
58
+ ## Development
59
+
60
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
61
+
62
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
63
+
64
+ ## Contributing
65
+
66
+ Bug reports and pull requests are welcome on GitHub at https://github.com/valthon/omniauth-nft.
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,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "eth"
4
+ module OmniAuth
5
+ module Nft
6
+ # Controller for OmniAuth::Strategies::Nft request phase.
7
+ class NftController < ActionController::Base
8
+ WALLET_SIG_SESSION_KEY = "nft__wallet_sig"
9
+ SIG_MESSAGE_SESSION_KEY = "nft__sig_message"
10
+
11
+ def request_phase
12
+ return unless address.present?
13
+
14
+ @address = address
15
+ return unless address_signed?
16
+
17
+ @sig = sig
18
+ checker = NftChecker.init(:opensea, testnet: true)
19
+ @list = checker.list_nfts({ slug: "untitled-collection-4919696" }, address)
20
+ end
21
+
22
+ private
23
+
24
+ def address
25
+ @address ||= begin
26
+ address = params.permit(:address)[:address].to_s
27
+ address if address =~ /\A0x[A-Za-z0-9]+\z/
28
+ end
29
+ end
30
+
31
+ def sig
32
+ @sig ||= begin
33
+ sig = params.permit(:sig)[:sig]
34
+ session[WALLET_SIG_SESSION_KEY] = sig if sig.present?
35
+ session[WALLET_SIG_SESSION_KEY]
36
+ end
37
+ end
38
+
39
+ def session_secret_message
40
+ session[SIG_MESSAGE_SESSION_KEY] ||= <<~SECRET
41
+ This is to verify you own the wallet with ID #{address}
42
+
43
+ | #{SecureRandom.uuid} |
44
+ SECRET
45
+ end
46
+
47
+ def address_signed?
48
+ @msg = session_secret_message
49
+ recovered_public_key = Eth::Key.personal_recover(@msg, sig)
50
+ recovered_address = Eth::Utils.public_key_to_address(recovered_public_key)
51
+ @sig_valid = recovered_address.casecmp(address).zero?
52
+ rescue StandardError
53
+ @sig_valid = false
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,152 @@
1
+ <style>
2
+ .metamask {
3
+ display: none;
4
+ }
5
+ .nft_request {
6
+ display: flex; flex-direction: column; text-align: center; width: 100%
7
+ }
8
+ .nft_request div {
9
+ margin: 16px;
10
+ }
11
+ button {
12
+ background-color: lightgreen;
13
+ padding: 2px 5px;
14
+ border: solid 1px black;
15
+ border-radius: 5px;
16
+ }
17
+ button:hover {
18
+ background-color: greenyellow;
19
+ }
20
+ button:active {
21
+ background-color: green;
22
+ color: white;
23
+ }
24
+ h2 {
25
+ display: block;
26
+ margin: 16px;
27
+ }
28
+ .nft_list {
29
+ margin: 10px;
30
+ display: flex;
31
+ flex-direction: row;
32
+ align-content: space-evenly;
33
+ }
34
+ .thumbnail {
35
+ margin: 5px;
36
+ border: thin solid #666;
37
+ max-height: 128px;
38
+ max-width: 128px;
39
+ }
40
+ textarea {
41
+ width: 32em;
42
+ height: 6em;
43
+ }
44
+ </style>
45
+ <script>
46
+ function set_address(id) {
47
+ document.querySelector('input[name=address]').value = id;
48
+ document.querySelector('form').submit();
49
+ }
50
+ function set_sig(sig) {
51
+ document.querySelector('textarea[name=sig]').value = sig;
52
+ document.querySelector('form').submit();
53
+ }
54
+
55
+ async function checkEthereum(ethereum) {
56
+ if (typeof ethereum !== 'undefined') {
57
+ const accounts = await ethereum.request({ method: 'eth_accounts' });
58
+ if (accounts && accounts[ 0 ] && accounts[0] != '<%= @address %>') {
59
+ set_address(accounts[ 0 ])
60
+ } else {
61
+ document.querySelectorAll('.metamask').forEach(x => x.style.display = 'block');
62
+ document.querySelector('.metamask button').addEventListener('click', async x => {
63
+ x.preventDefault();
64
+ const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
65
+ if (accounts && accounts[ 0 ]) {
66
+ set_address(accounts[ 0 ]);
67
+ }
68
+ })
69
+ }
70
+ }
71
+ }
72
+
73
+ async function tryEthereumSign(ethereum) {
74
+ if (typeof ethereum !== 'undefined') {
75
+ const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
76
+ const oldAccount = '<%= @address %>';
77
+ if (accounts && accounts[ 0 ] && accounts[0] != '<%= @address %>') {
78
+ set_address(accounts[ 0 ])
79
+ } else if (accounts && accounts[0]) {
80
+ const account = accounts[0]
81
+ const message = document.querySelector('textarea[name=sigmsg]').value;
82
+ const sig = await ethereum.request({ method: 'personal_sign', params: [message, account] });
83
+ if (sig) {
84
+ set_sig(sig);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ </script>
90
+
91
+ <% if @address.blank? %>
92
+
93
+ <div class="nft_request">
94
+ <div style="display:block">
95
+ Enter your wallet address:
96
+ <%= form_tag '', data: { turbo: false }, authenticity_token: params['authenticity_token'] do %>
97
+ <input class="address_input" name="address" type="text" placeholder="0x051c6..."/>
98
+ <button>Go!</button>
99
+ <% end %>
100
+ </div>
101
+ <div class="metamask">
102
+ - or -
103
+ </div>
104
+ <div class="metamask">
105
+ Connect with <button>MetaMask</button>
106
+ </div>
107
+ </div>
108
+
109
+ <script>
110
+ checkEthereum(window.ethereum);
111
+ </script>
112
+
113
+ <% elsif !@sig_valid %>
114
+
115
+ Please sign the following to continue...
116
+
117
+ <br/>
118
+
119
+ <textarea name=sigmsg disabled><%= @msg %></textarea>
120
+
121
+ <br/><br/>
122
+
123
+ <%= form_tag '', data: { turbo: false }, authenticity_token: params['authenticity_token'] do %>
124
+ <textarea placeholder="paste signature here..." name="sig"></textarea>
125
+ <%= hidden_field_tag :address, @address %>
126
+ <button>Go!</button>
127
+ <% end %>
128
+
129
+ <script>
130
+ tryEthereumSign(window.ethereum);
131
+ </script>
132
+
133
+ <%= link_to 'cancel', '/' %>
134
+
135
+ <script>
136
+
137
+ </script>
138
+
139
+ <% elsif @list.blank? %>
140
+ <% else %>
141
+ <h2>Which NFT would you like to authenticate with?</h2>
142
+ <row>
143
+ <ul class="nft_list" >
144
+ <% @list.each do |nft| %>
145
+ <%= button_to create_session_path(provider: 'nft'), params: { address: @address, nft_id: nft['token_id'], nft_contract: nft['asset_contract']['address'], sig: @sig, msg: @msg } do %>
146
+ <%= image_tag nft['image_thumbnail_url'] || nft['image_url'], title: nft['name'], alt: nft['token_id'], class: 'thumbnail' %>
147
+ <% end %>
148
+ <% end %>
149
+ </ul>
150
+ </row>
151
+
152
+ <% end %>
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "omniauth-nft"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAuth
4
+ module Nft
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAuth
4
+ module Nft
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "omniauth"
4
+ require "nft_checker"
5
+ require "eth"
6
+
7
+ module OmniAuth
8
+ module Strategies
9
+ # An OmniAuth strategy for authenticating via NFT ownership
10
+ class Nft
11
+ include OmniAuth::Strategy
12
+
13
+ option :checker_type, :opensea
14
+ option :checker_options, {}
15
+ option :nft_collection, {}
16
+ # option :form, OmniAuth::Nft::NftController.action(:request_phase)
17
+ # option :form, true
18
+
19
+ def request_phase
20
+ OmniAuth::Nft::NftController.action(:request_phase).call(env)
21
+ end
22
+ # def request_phase
23
+ # checker = NftChecker.init(options.checker_type, options.checker_options)
24
+ # # checker.list_nfts({slug: 'untitled-collection-4919696'}, '0x051c6B791044102Ae773e27FEA21480ed6D653F4'
25
+ # raise 'party 2!'
26
+ #
27
+ # end
28
+
29
+ attr_reader :nft, :address
30
+
31
+ uid do
32
+ "#{nft["asset_contract"]["address"]}::#{nft["token_id"]}"
33
+ end
34
+
35
+ info do
36
+ {
37
+ "name" => nft["name"],
38
+ "image" => nft["image_url"]
39
+ }
40
+ end
41
+
42
+ extra do
43
+ {
44
+ "wallet" => address,
45
+ "raw_info" => nft
46
+ }
47
+ end
48
+
49
+ def callback_phase # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
50
+ nft_params = ActionController::Parameters.new(request.params).permit(:address, :nft_id, :nft_contract, :sig)
51
+ return fail!(:no_wallet_address) if nft_params[:address].blank?
52
+ return fail!(:no_nft) if nft_params[:nft_id].blank? || nft_params[:nft_contract].blank?
53
+
54
+ nft_metadata = { contract_address: nft_params[:nft_contract], token_id: nft_params[:nft_id] }
55
+ nft = checker.fetch_nft_for_owner(nft_params[:address], nft_metadata)
56
+ return fail!(:invalid_nft) if nft.blank?
57
+
58
+ # At this point, we know the users owns the NFT, but not if it's part of our collection
59
+ return fail!(:not_in_collection) unless verify_collection(nft["collection"])
60
+
61
+ # Now we have verified the address owns the NFT and that the NFT is in our collection
62
+ return fail!(:invalid_signature) unless verify_signature(nft_params[:address], session["nft__sig_message"],
63
+ nft_params[:sig])
64
+
65
+ # And, completing the validation, we know the current user session owns the owning wallet
66
+ @address = nft_params[:address]
67
+ @nft = nft
68
+ super
69
+ end
70
+
71
+ private
72
+
73
+ def checker
74
+ @checker = NftChecker.init(options.checker_type, options.checker_options)
75
+ end
76
+
77
+ def verify_signature(address, message, signature)
78
+ recovered_public_key = Eth::Key.personal_recover(message, signature)
79
+ recovered_address = Eth::Utils.public_key_to_address(recovered_public_key)
80
+ recovered_address.casecmp(address).zero?
81
+ end
82
+
83
+ def verify_collection(nft_collection)
84
+ return false if nft_collection.blank?
85
+
86
+ options.nft_collection.each_key do |key|
87
+ return false unless nft_collection[key.to_s].casecmp(options.nft_collection[key]).zero?
88
+ end
89
+ true
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+ require "active_support/dependencies"
5
+
6
+ require "omniauth/nft/version"
7
+ require "omniauth/nft/rails"
8
+ require "omniauth/strategies/nft"
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/omniauth/nft/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "omniauth-nft"
7
+ spec.version = OmniAuth::Nft::VERSION
8
+ spec.authors = ["David J Parrott"]
9
+ spec.email = ["valthon@nothlav.net"]
10
+
11
+ spec.summary = "OmniAuth strategy for authenticating via NFT ownership"
12
+ spec.homepage = "https://github.com/valthon/omniauth-nft"
13
+ spec.required_ruby_version = ">= 2.6.0"
14
+
15
+ spec.metadata["rubygems_mfa_required"] = "true"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/valthon/omniauth-nft"
19
+ spec.metadata["changelog_uri"] = "https://github.com/valthon/omniauth-nft/blob/trunk/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency "eth", "~> 0.4"
33
+ spec.add_dependency "nft_checker", "~> 0.3"
34
+ spec.add_dependency "omniauth", "~> 2.0"
35
+
36
+ spec.add_development_dependency "rspec", "~> 3.0"
37
+ spec.add_development_dependency "rubocop", "~> 1.21"
38
+ spec.add_development_dependency "rubocop-rails", "~> 2.13"
39
+ spec.add_development_dependency "rubocop-rake", "~> 0.6"
40
+ spec.add_development_dependency "rubocop-rspec", "~> 0.6"
41
+
42
+ spec.add_runtime_dependency "rails", ">= 6.0.0"
43
+ end
metadata ADDED
@@ -0,0 +1,189 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-nft
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David J Parrott
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-01-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: eth
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nft_checker
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: omniauth
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.21'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.21'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.13'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.13'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.6'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.6'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.6'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.6'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rails
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 6.0.0
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 6.0.0
139
+ description:
140
+ email:
141
+ - valthon@nothlav.net
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".rspec"
147
+ - ".rubocop.yml"
148
+ - ".ruby-version"
149
+ - CHANGELOG.md
150
+ - Gemfile
151
+ - Gemfile.lock
152
+ - README.md
153
+ - Rakefile
154
+ - app/controllers/omni_auth/nft/nft_controller.rb
155
+ - app/views/omni_auth/nft/nft/request_phase.html.erb
156
+ - bin/console
157
+ - bin/setup
158
+ - lib/omniauth-nft.rb
159
+ - lib/omniauth/nft/rails.rb
160
+ - lib/omniauth/nft/version.rb
161
+ - lib/omniauth/strategies/nft.rb
162
+ - omniauth-nft.gemspec
163
+ homepage: https://github.com/valthon/omniauth-nft
164
+ licenses: []
165
+ metadata:
166
+ rubygems_mfa_required: 'true'
167
+ homepage_uri: https://github.com/valthon/omniauth-nft
168
+ source_code_uri: https://github.com/valthon/omniauth-nft
169
+ changelog_uri: https://github.com/valthon/omniauth-nft/blob/trunk/CHANGELOG.md
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: 2.6.0
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubygems_version: 3.2.32
186
+ signing_key:
187
+ specification_version: 4
188
+ summary: OmniAuth strategy for authenticating via NFT ownership
189
+ test_files: []