shimmer 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
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