shimmer 0.0.12 → 0.0.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '038bb182d1dad60da8ef884f677bf291baed9ffb2e4a14959cd20e55c43f7ff0'
4
- data.tar.gz: d5f6c1489a4b285202c2d93d3c9bfd7e8a5ac4b3a17688452e3315782b694511
3
+ metadata.gz: aa623a504a48b7e6f3a1c0d4ccf748b2068b8934288094524a8039c52588c384
4
+ data.tar.gz: 67ccb1224024c812aad171ddca3437c3d130fb1bbf71dc5c9326da143061be1c
5
5
  SHA512:
6
- metadata.gz: f10bade660c8ef17b62388e7ea9ac5d9b7bf40c658e744a1b9050a70bbabb7e2a360d1d10fdceb6fbffa43a002d94113efb3a794d8c233ad75387840f62d60bd
7
- data.tar.gz: fbdba908476796302ef9db666285fe57e39a69ae095374d11291cf2ea43c7bfe3bc1c5a6d74c23f2d46e815806ff6f95fe3b1c594834196d7169fe171b605f8a
6
+ metadata.gz: 9de1f128d5baafb0c5effb645c59ccecec08b7c7319761802cfb8ed7bbeb55cc01abb9f365f85e0397ceff692e835dc5131683bac01f514619bf1623be831c37
7
+ data.tar.gz: 856718e8800d539c1c31c9b867768c9db4939fe979019f44e6eebf6203cb1d60f8daca6ce42d3b21cab1027d00106345a531cb28bd912bcdea31028885042803
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ class AppleProvider < Provider
6
+ self.token_column = :apple_id
7
+
8
+ private
9
+
10
+ def request_details(params)
11
+ name = params[:user] ? JSON.parse(params[:user])["name"] : {}
12
+ headers = {
13
+ 'Content-Type': "application/x-www-form-urlencoded"
14
+ }
15
+ form = {
16
+ grant_type: "authorization_code",
17
+ code: params[:code],
18
+ client_id: ENV.fetch("APPLE_BUNDLE_ID"),
19
+ client_secret: ENV.fetch("APPLE_CLIENT_SECRET"),
20
+ scope: "name email"
21
+ }
22
+ response = HTTParty.post("https://appleid.apple.com/auth/token", body: URI.encode_www_form(form), headers: headers)
23
+ raise InvalidTokenError, "Login check failed: #{response.body}" unless response.ok?
24
+
25
+ token = JWT.decode(response["id_token"], nil, false).first
26
+ UserDetails.new token: token["sub"], email: token["email"], first_name: name["firstName"], last_name: name["lastName"]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ module Authenticating
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ before_action :authenticate
10
+ helper_method :current_user
11
+
12
+ def require_login
13
+ redirect_to login_path unless current_user
14
+ end
15
+
16
+ def current_user
17
+ ::Current.user
18
+ end
19
+
20
+ def login(device:)
21
+ ::Current.device = device
22
+ cookies.encrypted[:device_token] = {value: device.token, expires: 2.years.from_now}
23
+ end
24
+
25
+ def logout
26
+ cookies.delete :device_token
27
+ end
28
+
29
+ private
30
+
31
+ def authenticate
32
+ ::Current.device = cookies.encrypted[:device_token].presence&.then { |e| ::Device.find_by token: e }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ module Current
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attribute :device
10
+
11
+ delegate :user, to: :device, allow_nil: true
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ class DevProvider < Provider
6
+ def login(email:, user_agent: nil, ip: nil)
7
+ user = model.find_or_create_by!(email: email)
8
+ device = user.devices.create! user_agent: user_agent
9
+ log_login(user, device_id: device.id, user_agent: user_agent, ip: ip)
10
+ device
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ module Device
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ has_secure_token
10
+
11
+ def name
12
+ [browser.platform.name, browser.name].join(" ")
13
+ end
14
+
15
+ private
16
+
17
+ def browser
18
+ @browser ||= Browser.new user_agent
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ class GoogleProvider < Provider
6
+ self.token_column = :google_id
7
+
8
+ private
9
+
10
+ def request_details(params)
11
+ payload = GoogleIDToken::Validator.new.check(params[:credential], ENV.fetch("GOOGLE_CLIENT_ID"))
12
+ UserDetails.new token: payload["sub"], email: payload["email"], first_name: payload["given_name"].presence, last_name: payload["family_name"].presence
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ module User
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ def authenticate!(user_agent: nil, ip: nil)
10
+ Provider.new(self.class).create_device(user: self, user_agent: user_agent, ip: ip)
11
+ end
12
+ end
13
+
14
+ class_methods do
15
+ def login!(provider:, **attributes)
16
+ "Shimmer::Auth::#{provider.to_s.classify}Provider".constantize
17
+ .new(self).login(**attributes)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ class Provider
6
+ class InvalidTokenError < StandardError; end
7
+ UserDetails = Struct.new(:token, :email, :first_name, :last_name, keyword_init: true)
8
+ attr_reader :model
9
+ cattr_accessor :token_column
10
+
11
+ def initialize(model)
12
+ @model = model
13
+ end
14
+
15
+ def login(params:, user_agent: nil, ip: nil)
16
+ user = fetch_user request_details(params)
17
+ create_device user: user, user_agent: user_agent, ip: ip
18
+ end
19
+
20
+ def create_device(user:, user_agent: nil, ip: nil)
21
+ user.devices.create!(user_agent: user_agent).tap do |device|
22
+ log_login(user, device_id: device.id, user_agent: user_agent, ip: ip)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def log_login(user, device_id:, user_agent: nil, ip: nil)
29
+ return unless user.respond_to? :publish
30
+
31
+ user.publish :login, provider: self.class.name.demodulize.underscore, device_id: device_id, user_agent: user_agent, ip: ip
32
+ end
33
+
34
+ def fetch_user(details)
35
+ user = model.find_by(token_column => details.token) || model.find_by(email: details.email) || model.new
36
+ user[token_column] ||= details.token
37
+ user.email ||= details.email
38
+ user.first_name ||= details.first_name
39
+ user.last_name ||= details.last_name
40
+ user.save! if user.changed?
41
+ user
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ Dir["#{File.expand_path("./auth", __dir__)}/*"].sort.each { |e| require e }
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :auth do
4
+ desc "Generates a Sign in with Apple Token"
5
+ task :apple_token do
6
+ ecdsa_key = OpenSSL::PKey::EC.new IO.read ".apple-key.p8"
7
+ headers = {
8
+ "kid" => Shimmer::Config.instance.apple_key_id!
9
+ }
10
+ claims = {
11
+ "iss" => Shimmer::Config.instance.apple_team_id!,
12
+ "iat" => Time.now.to_i,
13
+ "exp" => 180.days.from_now.to_i,
14
+ "aud" => "https://appleid.apple.com",
15
+ "sub" => Shimmer::Config.instance.apple_bundle_id!
16
+ }
17
+ puts JWT.encode claims, ecdsa_key, "ES256", headers
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ class Config
5
+ include Singleton
6
+ class MissingConfigError < StandardError; end
7
+
8
+ def method_missing(method_name)
9
+ method_name = method_name.to_s
10
+ type = :string
11
+ key = method_name.delete_suffix("!").delete_suffix("?")
12
+ required = method_name.end_with?("!")
13
+ type = :bool if method_name.end_with?("?")
14
+ value = ENV[key.upcase].presence
15
+ value ||= Rails.application.credentials.send(key)
16
+ raise MissingConfigError, "#{key.upcase} environment value is missing" if required && value.blank?
17
+
18
+ coerce value, type
19
+ end
20
+
21
+ def respond_to_missing?(method_name)
22
+ true
23
+ end
24
+
25
+ private
26
+
27
+ def coerce(value, type)
28
+ return !value.in?(["n", "0", "no", "false"]) if type == :bool && value.is_a?(String)
29
+
30
+ value
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Shimmer
4
- VERSION = "0.0.12"
4
+ VERSION = "0.0.13"
5
5
  end
data/lib/shimmer.rb CHANGED
@@ -6,6 +6,7 @@ Dir["#{File.expand_path("../lib/shimmer/middlewares", __dir__)}/*"].sort.each {
6
6
  Dir["#{File.expand_path("../lib/shimmer/controllers", __dir__)}/*"].sort.each { |e| require e }
7
7
  Dir["#{File.expand_path("../lib/shimmer/jobs", __dir__)}/*"].sort.each { |e| require e }
8
8
  Dir["#{File.expand_path("../lib/shimmer/utils", __dir__)}/*"].sort.each { |e| require e }
9
+ require_relative "shimmer/auth"
9
10
 
10
11
  module Shimmer
11
12
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shimmer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jens Ravens
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-06-08 00:00:00.000000000 Z
11
+ date: 2022-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -135,13 +135,23 @@ files:
135
135
  - bin/solargraph
136
136
  - config/rubocop_base.yml
137
137
  - lib/shimmer.rb
138
+ - lib/shimmer/auth.rb
139
+ - lib/shimmer/auth/apple_provider.rb
140
+ - lib/shimmer/auth/authenticating.rb
141
+ - lib/shimmer/auth/current.rb
142
+ - lib/shimmer/auth/dev_provider.rb
143
+ - lib/shimmer/auth/device.rb
144
+ - lib/shimmer/auth/google_provider.rb
145
+ - lib/shimmer/auth/user.rb
138
146
  - lib/shimmer/controllers/files_controller.rb
139
147
  - lib/shimmer/controllers/sitemaps_controller.rb
140
148
  - lib/shimmer/jobs/sitemap_job.rb
141
149
  - lib/shimmer/middlewares/cloudflare.rb
142
150
  - lib/shimmer/railtie.rb
151
+ - lib/shimmer/tasks/auth.rake
143
152
  - lib/shimmer/tasks/db.rake
144
153
  - lib/shimmer/tasks/lint.rake
154
+ - lib/shimmer/utils/config.rb
145
155
  - lib/shimmer/utils/file_helper.rb
146
156
  - lib/shimmer/utils/file_proxy.rb
147
157
  - lib/shimmer/utils/localizable.rb