shakha 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 366829b83286435b8a3ff8a9b74cdfe319e072768efabbbfa0777d3ce7a9bb6b
4
- data.tar.gz: 5aafc4d8d1db8bf6287c40ffea5dbb614903e288ddfeb35ae4d1c1e0b0ecff34
3
+ metadata.gz: 7f2fb69e4f483c981312b4211c9205e9ae5d0d100a0d3d8f0d61f693478c8369
4
+ data.tar.gz: 6dfb50d826c90ea54ed5453d7eebad7a94bf93cea02a1bb407fbd69a61da421e
5
5
  SHA512:
6
- metadata.gz: 6a7982ee421c97e9f66eb5eceb29dddd2159806d37d37bb759e84c80686390f395703adfd8b881c7a0d5eafa3c5e2bc969844ad6375ce4a04572b5e4758b5f75
7
- data.tar.gz: f41c800d89c409fbcea81ab122d7f9e59028777b15643ec006ff0b6e0cddb7adcc621c463cdc538b088e171f9f4eb460cf5197b48b361700a041373c5899b2f7
6
+ metadata.gz: 1bac5c8e1d92fb2997713d5ad19d7b16327fc21ec7e7520adf09d736bbe2c15c1cb72ef7c859798cba62a8a012819a1fb4863bd6c49830cd10d98f3082d4afc9
7
+ data.tar.gz: 8e9ae9652bb51b48bc30623305113e75c2a1c56676ec17a4e9503f4dc9694f55cf87d1d58fa9a2aaee956e47fa8ee1ecdc35024f3500c616e228942f42efbfac
data/README.md CHANGED
@@ -28,12 +28,13 @@ class CreateShakhaTables < ActiveRecord::Migration[7.1]
28
28
 
29
29
  create_table :shakha_users do |t|
30
30
  t.references :client, null: false, foreign_key: { to_table: :shakha_clients }
31
- t.string :pairwise_sub, null: false
31
+ t.string :provider, null: false
32
+ t.string :uid, null: false
32
33
  t.string :email
33
34
  t.string :name
34
35
  t.string :picture
35
36
  t.timestamps
36
- t.index :pairwise_sub, unique: true
37
+ t.index [:provider, :uid], unique: true
37
38
  t.index :email
38
39
  end
39
40
 
@@ -41,12 +42,10 @@ class CreateShakhaTables < ActiveRecord::Migration[7.1]
41
42
  t.references :user, foreign_key: { to_table: :shakha_users }
42
43
  t.references :client, null: false, foreign_key: { to_table: :shakha_clients }
43
44
  t.string :token, null: false
44
- t.string :jti, null: false
45
45
  t.string :ip_address
46
46
  t.string :user_agent
47
47
  t.timestamps
48
48
  t.index :token, unique: true
49
- t.index :jti, unique: true
50
49
  t.index :created_at
51
50
  end
52
51
  end
@@ -14,7 +14,7 @@ module Shakha
14
14
 
15
15
  private
16
16
 
17
- def invalid_csrf_token(exception)
17
+ def invalid_csrf_token(_exception)
18
18
  render json: { error: "Invalid CSRF token" }, status: :unprocessable_entity
19
19
  end
20
20
  end
@@ -6,199 +6,92 @@ require "uri"
6
6
  module Shakha
7
7
  class AuthController < ApplicationController
8
8
  include PKCEMixin
9
- include Auditable
10
9
 
11
- skip_before_action :verify_authenticity_token, only: [:callback, :token]
10
+ skip_before_action :verify_authenticity_token, only: [:callback]
12
11
 
13
12
  def new
14
13
  @client = find_or_create_client
15
14
  @return_to = sanitize_return_to(params[:return_to])
15
+ @providers = Shakha.config.providers
16
16
  end
17
17
 
18
18
  def authorize
19
- params[:return_to] = sanitize_return_to(params[:return_to])
19
+ provider = resolve_provider
20
20
  pkce = create_pkce_bundle
21
- @client = find_or_create_client
22
21
 
23
- google_auth_url = build_google_auth_url(pkce)
22
+ redirect_uri = "#{Shakha.config.app_origin}/auth/shakha/#{provider.provider_name}/callback"
23
+ auth_url = provider.authorize_url(
24
+ state: pkce[:state],
25
+ code_challenge: pkce[:challenge],
26
+ redirect_uri: redirect_uri
27
+ )
24
28
 
25
- redirect_to google_auth_url, allow_other_host: true
29
+ redirect_to auth_url, allow_other_host: true
26
30
  end
27
31
 
28
32
  def callback
33
+ provider = resolve_provider
29
34
  pkce_result = verify_pkce!(params[:state])
30
- exchange_code_for_tokens(params[:code], pkce_result[:verifier], pkce_result[:return_to], pkce_result[:nonce])
31
- rescue PKCEError, GoogleOAuthError => e
32
- ActiveSupport::Notifications.instrument("shakha.sign_in_failed", {
33
- reason: e.class.name,
34
- ip: request.remote_ip
35
- })
36
- Rails.logger.warn("[Shakha] Auth error: #{e.class}: #{e.message}")
37
- redirect_to "/auth/shakha/error?message=#{URI.encode_www_form_component(user_facing_error(e))}"
38
- end
39
-
40
- def token
41
- code = params[:code]
42
- verifier = params[:code_verifier]
43
35
 
44
- raise PKCEError, "Missing code" unless code
45
- raise PKCEError, "Missing code_verifier" unless verifier
46
-
47
- id_token = exchange_code_for_id_token(code, verifier)
36
+ token_response = provider.exchange_code(
37
+ code: params[:code],
38
+ code_verifier: pkce_result[:verifier],
39
+ redirect_uri: "#{Shakha.config.app_origin}/auth/shakha/#{provider.provider_name}/callback"
40
+ )
48
41
 
49
- render json: {
50
- id_token: id_token,
51
- pairwise_sub: id_token_payload(id_token)[:sub],
52
- expires_in: 24.hours.to_i
53
- }
54
- rescue PKCEError, JWTError, GoogleOAuthError => e
55
- render json: { error: e.message }, status: :unauthorized
56
- end
42
+ identity = provider.identity_from_response(token_response)
43
+ user = find_or_create_user(provider.provider_name, identity)
44
+ session_record = create_session(user)
45
+ set_session_cookie(session_record)
46
+ redirect_to build_return_url(pkce_result[:return_to], session_record)
57
47
 
58
- def error
59
- @message = params[:message] || "Authentication failed"
48
+ rescue PKCEError, OAuthError => e
49
+ handle_auth_failure(e, pkce_result)
60
50
  end
61
51
 
62
- private
63
-
64
- def sanitize_return_to(raw)
65
- return "/" if raw.blank?
66
-
67
- uri = URI.parse(raw)
68
- app_host = URI.parse(Shakha.config.app_origin).host
69
-
70
- # Must have a path
71
- return "/" unless uri.path.present? && uri.path.start_with?("/")
52
+ def destroy
53
+ current_session&.destroy
54
+ cookies.delete(:shakha_session_token)
72
55
 
73
- # If external host, must be in allowed origins
74
- if uri.host.present? && uri.host != app_host && !allowed_origin?(uri.origin)
75
- return "/"
56
+ respond_to do |format|
57
+ format.html { redirect_to params[:return_to].presence || "/" }
58
+ format.json { render json: { status: "signed_out" } }
76
59
  end
77
-
78
- raw
79
- rescue URI::InvalidURIError
80
- "/"
81
- end
82
-
83
- def allowed_origin?(origin)
84
- Shakha.config.allowed_redirect_origins&.include?(origin) || false
85
60
  end
86
61
 
87
- def app_origin_host
88
- URI.parse(Shakha.config.app_origin).host
62
+ def error
63
+ @message = params[:message] || "Authentication failed"
89
64
  end
90
65
 
91
- def client_origin_host
92
- URI.parse(Shakha.config.service_base_url).host
93
- rescue URI::InvalidURIError
94
- nil
95
- end
66
+ private
96
67
 
97
- def user_facing_error(exception)
98
- case exception
99
- when PKCEError
100
- "Authentication failed. Please try again."
101
- when GoogleOAuthError
102
- "Unable to sign in with Google. Please try again later."
103
- else
104
- "An unexpected error occurred. Please try again."
105
- end
68
+ def resolve_provider
69
+ provider_name = (params[:provider] || :google).to_sym
70
+ Shakha::Providers.resolve(provider_name)
106
71
  end
107
72
 
108
- def find_or_create_client
109
- origin = request.origin || Shakha.config.app_origin
110
- origin_uri = URI.parse(origin).origin
111
-
112
- if Shakha.config.embedded?
113
- Shakha::Client.find_or_create_by!(origin: origin_uri) do |client|
114
- client.name = URI.parse(origin).host
115
- end
116
- else
117
- Shakha::Client.find_by!(origin: origin_uri)
73
+ def find_or_create_user(provider_name, identity)
74
+ Shakha::User.find_or_create_by!(
75
+ provider: provider_name.to_s,
76
+ uid: identity[:uid]
77
+ ) do |user|
78
+ user.client = find_or_create_client
79
+ user.email = identity[:email]
80
+ user.name = identity[:name]
81
+ user.picture = identity[:picture]
118
82
  end
119
- rescue ActiveRecord::RecordNotFound
120
- raise ConfigurationError, "Unknown client origin: #{origin_uri}. Register this origin in shakha_clients first."
121
- end
122
-
123
- def build_google_auth_url(pkce)
124
- client_id = Shakha.config.google_client_id || ENV["GOOGLE_CLIENT_ID"]
125
- base_url = Shakha.config.service_base_url || "http://localhost:3000"
126
- redirect_uri = "#{base_url}/auth/shakha/callback"
127
-
128
- scopes = ["openid", "email", "profile"].join(" ")
129
- scopes += " https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile" if params[:request_pii]
130
-
131
- params = {
132
- client_id: client_id,
133
- redirect_uri: redirect_uri,
134
- response_type: "code",
135
- scope: scopes,
136
- code_challenge: pkce[:challenge],
137
- code_challenge_method: "S256",
138
- state: pkce[:state],
139
- nonce: pkce[:nonce],
140
- access_type: "offline",
141
- prompt: "consent"
142
- }
143
-
144
- URI.parse("https://accounts.google.com/o/oauth2/v2/auth").tap do |uri|
145
- uri.query = URI.encode_www_form(params)
146
- end.to_s
147
83
  end
148
84
 
149
- def exchange_code_for_tokens(code, verifier, return_to = "/", expected_nonce = nil)
150
- client_id = Shakha.config.google_client_id || ENV["GOOGLE_CLIENT_ID"]
151
- client_secret = Shakha.config.google_client_secret || ENV["GOOGLE_CLIENT_SECRET"]
152
- base_url = Shakha.config.service_base_url || "http://localhost:3000"
153
- redirect_uri = "#{base_url}/auth/shakha/callback"
154
-
155
- response = http_post(
156
- "https://oauth2.googleapis.com/token",
157
- {
158
- code: code,
159
- client_id: client_id,
160
- client_secret: client_secret,
161
- redirect_uri: redirect_uri,
162
- grant_type: "authorization_code",
163
- code_verifier: verifier
164
- }
165
- )
166
-
167
- tokens = JSON.parse(response.body)
168
- id_token = tokens["id_token"]
169
- access_token = tokens["access_token"]
170
-
171
- raise GoogleOAuthError, "No id_token received" unless id_token
172
-
173
- payload = decode_id_token(id_token)
174
- google_sub = payload["sub"]
175
-
176
- # Verify nonce (OIDC replay protection)
177
- if expected_nonce && payload["nonce"] != expected_nonce
178
- raise GoogleOAuthError, "Nonce mismatch"
179
- end
180
-
181
- client = find_or_create_client
182
- pairwise_sub = Shakha.derive_pairwise_sub(google_sub, client.client_id)
183
-
184
- user = Shakha::User.find_or_initialize_by(pairwise_sub: pairwise_sub, client: client)
185
-
186
- if payload["email"]
187
- user.assign_attributes(
188
- email: payload["email"],
189
- name: payload["name"],
190
- picture: payload["picture"]
191
- )
192
- end
193
- user.save!
194
-
195
- session_record = Shakha::Session.create!(
85
+ def create_session(user)
86
+ Shakha::Session.create!(
196
87
  user: user,
197
- client: client,
88
+ client: find_or_create_client,
198
89
  ip_address: request.remote_ip,
199
90
  user_agent: request.user_agent
200
91
  )
92
+ end
201
93
 
94
+ def set_session_cookie(session_record)
202
95
  cookies.encrypted[:shakha_session_token] = {
203
96
  value: session_record.token,
204
97
  httponly: true,
@@ -206,61 +99,66 @@ module Shakha
206
99
  same_site: :lax,
207
100
  expires: Shakha.config.session_lifetime.from_now
208
101
  }
209
-
210
- redirect_to sanitize_return_to(return_to)
211
102
  end
212
103
 
213
- def exchange_code_for_id_token(code, verifier)
214
- client_id = Shakha.config.google_client_id || ENV["GOOGLE_CLIENT_ID"]
215
- client_secret = Shakha.config.google_client_secret || ENV["GOOGLE_CLIENT_SECRET"]
216
- redirect_uri = "#{Shakha.config.service_base_url}/auth/shakha/callback"
104
+ def build_return_url(return_to, session_record)
105
+ uri = URI.parse(return_to || "/")
106
+ existing = URI.decode_www_form(uri.query || "").to_h
107
+ existing["token"] = session_record.token
108
+ existing["expires_at"] = session_record.expires_at.iso8601
109
+ uri.query = URI.encode_www_form(existing)
110
+ uri.to_s
111
+ end
217
112
 
218
- response = http_post(
219
- "https://oauth2.googleapis.com/token",
220
- {
221
- code: code,
222
- client_id: client_id,
223
- client_secret: client_secret,
224
- redirect_uri: redirect_uri,
225
- grant_type: "authorization_code",
226
- code_verifier: verifier
227
- }
228
- )
113
+ def handle_auth_failure(exception, pkce_result)
114
+ return_to = pkce_result&.dig(:return_to) || "/"
229
115
 
230
- tokens = JSON.parse(response.body)
231
- tokens["id_token"] || raise(GoogleOAuthError, "No id_token in response")
116
+ if request.format.json? || api_request?
117
+ render json: { error: user_facing_error(exception) }, status: :unauthorized
118
+ else
119
+ redirect_to "#{return_to}?error=#{URI.encode_www_form_component(user_facing_error(exception))}"
120
+ end
232
121
  end
233
122
 
234
- def id_token_payload(id_token)
235
- JWT.decode(id_token, nil, false)[0]
123
+ def api_request?
124
+ request.headers["Accept"]&.include?("application/json")
236
125
  end
237
126
 
238
- def decode_id_token(id_token)
239
- JWT.decode(id_token, nil, false)[0]
240
- end
127
+ def sanitize_return_to(raw)
128
+ return "/" if raw.blank?
129
+
130
+ uri = URI.parse(raw)
131
+ app_host = URI.parse(Shakha.config.app_origin).host
132
+
133
+ return "/" unless uri.path.present? && uri.path.start_with?("/")
241
134
 
242
- def http_post(url, body)
243
- uri = URI.parse(url)
244
- http = Net::HTTP.new(uri.host, uri.port)
245
- http.use_ssl = uri.scheme == "https"
246
- http.open_timeout = 5
247
- http.read_timeout = 10
135
+ if uri.host.present? && uri.host != app_host && !allowed_origin?(uri.origin)
136
+ return "/"
137
+ end
248
138
 
249
- request = Net::HTTP::Post.new(uri.request_uri)
250
- request["Content-Type"] = "application/x-www-form-urlencoded"
251
- request.body = URI.encode_www_form(body)
139
+ raw
140
+ rescue URI::InvalidURIError
141
+ "/"
142
+ end
252
143
 
253
- response = http.request(request)
144
+ def allowed_origin?(origin)
145
+ Shakha.config.allowed_redirect_origins&.include?(origin) || false
146
+ end
254
147
 
255
- unless response.is_a?(Net::HTTPSuccess)
256
- Rails.logger.error("[Shakha] Google API error: HTTP #{response.code} #{response.body.truncate(500)}")
257
- raise GoogleOAuthError, "Google returned HTTP #{response.code}"
148
+ def find_or_create_client
149
+ origin = request.origin || Shakha.config.app_origin
150
+ origin_uri = URI.parse(origin).origin
151
+ Shakha::Client.find_or_create_by!(origin: origin_uri) do |client|
152
+ client.name = URI.parse(origin).host
258
153
  end
154
+ end
259
155
 
260
- response
261
- rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, SocketError => e
262
- Rails.logger.error("[Shakha] Network error contacting Google: #{e.message}")
263
- raise GoogleOAuthError, "Unable to reach Google authentication service"
156
+ def user_facing_error(exception)
157
+ case exception
158
+ when PKCEError then "Authentication failed. Please try again."
159
+ when OAuthError then "Unable to sign in. Please try again later."
160
+ else "An unexpected error occurred. Please try again."
161
+ end
264
162
  end
265
163
  end
266
- end
164
+ end
@@ -2,76 +2,31 @@
2
2
 
3
3
  module Shakha
4
4
  class SessionController < ApplicationController
5
- include Auditable
6
- skip_before_action :verify_authenticity_token, only: [:check]
7
-
8
- def index
9
- return render json: { error: "Authentication required" }, status: :unauthorized unless signed_in?
10
-
11
- sessions = current_user.sessions.active.order(created_at: :desc)
5
+ def show
6
+ unless signed_in?
7
+ return render json: { error: "Authentication required" }, status: :unauthorized
8
+ end
12
9
 
13
10
  render json: {
14
- current_token: current_session.token,
15
- sessions: sessions.map { |s|
16
- {
17
- id: s.id,
18
- token: s.token,
19
- created_at: s.created_at.iso8601,
20
- expires_at: s.expires_at.iso8601,
21
- current: s.token == current_session.token
22
- }
11
+ user: {
12
+ id: current_user.id,
13
+ email: current_user.email,
14
+ name: current_user.name,
15
+ picture: current_user.picture,
16
+ provider: current_user.provider
17
+ },
18
+ session: {
19
+ expires_at: current_session.expires_at.iso8601
23
20
  }
24
21
  }
25
22
  end
26
23
 
27
- def show
28
- render json: {
29
- user_id: current_user&.pairwise_sub,
30
- email: current_user&.email,
31
- name: current_user&.name,
32
- expires_at: current_session&.expires_at&.iso8601
33
- }
34
- end
35
-
36
24
  def check
37
25
  if signed_in?
38
26
  render json: { status: "active" }
39
27
  else
40
- render json: {
41
- status: "login_required",
42
- reason: "no_session"
43
- }, status: :unauthorized
44
- end
45
- end
46
-
47
- def destroy
48
- current_session&.destroy
49
- cookies.delete(:shakha_session_token)
50
-
51
- respond_to do |format|
52
- format.html { redirect_to params[:return_to].presence || "/" }
53
- format.json { render json: { status: "signed_out" } }
28
+ render json: { status: "expired" }, status: :unauthorized
54
29
  end
55
30
  end
56
-
57
- def list
58
- return redirect_to "/auth/shakha" unless signed_in?
59
-
60
- @sessions = current_user.sessions.active.order(created_at: :desc)
61
- @current_token = current_session&.token
62
- end
63
-
64
- def revoke
65
- return render json: { error: "Authentication required" }, status: :unauthorized unless signed_in?
66
-
67
- session = current_user.sessions.find(params[:id])
68
- session.destroy
69
-
70
- cookies.delete(:shakha_session_token) if session.token == current_session&.token
71
-
72
- log_session_revoked(session)
73
-
74
- render json: { status: "revoked" }
75
- end
76
31
  end
77
- end
32
+ end
@@ -9,10 +9,6 @@ module Shakha
9
9
 
10
10
  validates :origin, presence: true, uniqueness: true
11
11
 
12
- def client_id
13
- "origin:#{origin}"
14
- end
15
-
16
12
  def self.find_by_origin!(origin)
17
13
  find_by!(origin: URI.parse(origin).origin)
18
14
  end
@@ -8,7 +8,6 @@ module Shakha
8
8
  belongs_to :client, class_name: "Shakha::Client"
9
9
 
10
10
  before_create :generate_token
11
- before_create :generate_jti
12
11
 
13
12
  scope :active, -> { where("created_at > ?", Shakha.config.session_lifetime.ago) }
14
13
 
@@ -25,9 +24,5 @@ module Shakha
25
24
  def generate_token
26
25
  self.token ||= SecureRandom.urlsafe_base64(32)
27
26
  end
28
-
29
- def generate_jti
30
- self.jti ||= SecureRandom.uuid
31
- end
32
27
  end
33
28
  end
@@ -7,11 +7,9 @@ module Shakha
7
7
  belongs_to :client, class_name: "Shakha::Client"
8
8
  has_many :sessions, class_name: "Shakha::Session", dependent: :destroy
9
9
 
10
- validates :pairwise_sub, presence: true
10
+ validates :provider, presence: true
11
+ validates :uid, presence: true
12
+ validates :uid, uniqueness: { scope: :provider }
11
13
  validates :email, uniqueness: { scope: :client_id }, allow_blank: true
12
-
13
- def can_access?(resource)
14
- true
15
- end
16
14
  end
17
15
  end
@@ -16,25 +16,13 @@
16
16
  </div>
17
17
 
18
18
  <div class="sh-card__body">
19
- <%= link_to shakha.authorize_path(request_pii: 1),
20
- class: "sh-btn sh-btn--google",
21
- data: { turbo: false } do %>
22
- <svg class="sh-btn__icon" viewBox="0 0 18 18" aria-hidden="true">
23
- <path fill="#4285F4" d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.875 2.684-6.615z"/>
24
- <path fill="#34A853" d="M9 18c2.43 0 4.467-.806 5.956-2.184l-2.908-2.258c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18z"/>
25
- <path fill="#FBBC05" d="M3.964 10.71A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.042l3.007-2.332z"/>
26
- <path fill="#EA4335" d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z"/>
27
- </svg>
28
- Continue with Google
19
+ <% @providers.each do |provider| %>
20
+ <%= link_to shakha.send("#{provider}_authorize_path"),
21
+ class: "sh-btn sh-btn--#{provider}",
22
+ data: { turbo: false } do %>
23
+ Continue with <%= provider.to_s.titleize %>
24
+ <% end %>
29
25
  <% end %>
30
-
31
- <div class="sh-divider">or</div>
32
-
33
- <p class="sh-text-center" style="color: var(--sh-text-tertiary); font-size: var(--sh-text-sm);">
34
- By signing in, you agree to our
35
- <a href="#">Terms</a> and
36
- <a href="#">Privacy Policy</a>.
37
- </p>
38
26
  </div>
39
27
 
40
28
  <div class="sh-card__footer">
data/lib/shakha/config.rb CHANGED
@@ -3,43 +3,19 @@
3
3
  module Shakha
4
4
  class Config
5
5
  attr_accessor :app_origin,
6
- :service_url,
7
- :service_secret,
8
6
  :google_client_id,
9
7
  :google_client_secret,
10
- :issuer,
8
+ :github_client_id,
9
+ :github_client_secret,
10
+ :providers,
11
11
  :session_lifetime,
12
- :signing_key,
13
- :verification_key,
14
- :key_id,
15
12
  :rate_limiting_enabled,
16
13
  :allowed_redirect_origins
17
14
 
18
15
  def initialize
19
16
  @session_lifetime = 30.days
20
- @issuer = "https://shakha.dev"
21
17
  @rate_limiting_enabled = false
22
- end
23
-
24
- def embedded?
25
- service_url.blank?
26
- end
27
-
28
- def service_base_url
29
- return app_origin if embedded?
30
-
31
- service_url.chomp("/")
32
- end
33
-
34
- def client_id
35
- return @client_id if defined?(@client_id)
36
-
37
- origin = URI.parse(app_origin).origin
38
- @client_id = "origin:#{origin}"
39
- end
40
-
41
- def audience
42
- client_id
18
+ @providers = [:google]
43
19
  end
44
20
  end
45
- end
21
+ end
@@ -5,10 +5,9 @@ module Shakha
5
5
  class << self
6
6
  def validate!(config)
7
7
  missing = []
8
- missing << "SHAKHA_APP_ORIGIN" unless config.app_origin.present?
8
+ missing << "APP_ORIGIN" unless config.app_origin.present?
9
9
  missing << "GOOGLE_CLIENT_ID" unless config.google_client_id.present?
10
10
  missing << "GOOGLE_CLIENT_SECRET" unless config.google_client_secret.present?
11
- missing << "SHAKHA_SERVICE_SECRET" unless config.service_secret.present?
12
11
 
13
12
  unless missing.empty?
14
13
  message = "Shakha: missing required configuration: #{missing.join(', ')}"