clowk 0.5.1 → 0.6.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 +4 -4
- data/README.md +111 -2
- data/clowk.gemspec +2 -2
- data/lib/clowk/authenticable.rb +24 -2
- data/lib/clowk/credentials.rb +130 -0
- data/lib/clowk/jwks.rb +2 -2
- data/lib/clowk/jwt_verifier.rb +5 -2
- data/lib/clowk/sdk/client.rb +2 -2
- data/lib/clowk/subdomain.rb +2 -2
- data/lib/clowk/version.rb +1 -1
- data/lib/clowk.rb +5 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1593747e0040a0056565a40b2bf1699d8242af1d253751ce3ea5c86949ff1f7a
|
|
4
|
+
data.tar.gz: 33147a1e6f88c7970d52bf33403566c09309315c53b8e080416aaece4f1ecc2a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0cf89e539def821193dd451c8902e581423f103acd8881ce67d7eadb9cfd9e2e69302a265769679eb5a906593a89253f4781cf625546ee3bbd3d8d012d15584a
|
|
7
|
+
data.tar.gz: f4993722c5349f80a7a10f39cff94d3b76b4909b700272f23458bca7d227fa263258c78b01cf511e2cb5d30640dae84a991fd686939fc4fbfcd2cee7b2e5e757
|
data/README.md
CHANGED
|
@@ -107,13 +107,101 @@ Important settings:
|
|
|
107
107
|
| `publishable_key` | Preferred for auth URL resolution. The gem resolves the latest instance URL from it before sign in/sign up. Also the default `audience`. |
|
|
108
108
|
| `subdomain_url` | Fallback auth domain when you do not want publishable-key-based resolution. |
|
|
109
109
|
| `jwks_url` | Where to fetch Clowk's public keys. Defaults to `<auth domain>/.well-known/jwks.json`. |
|
|
110
|
-
| `audience` | Expected `aud` claim on RS256 tokens. Defaults to `publishable_key`. Set to `
|
|
110
|
+
| `audience` | Expected `aud` claim on RS256 tokens. Defaults to `publishable_key`. Set to `false` to skip the check. |
|
|
111
111
|
| `prefix_by` | Prefix used to generate helper names. Default: `:clowk`. |
|
|
112
112
|
| `mount_path` | Local mount prefix used by helper path generation. Default: `/clowk`. |
|
|
113
113
|
| `callback_path` | Callback route Clowk redirects back to. Default: `/clowk/oauth/callback`. |
|
|
114
114
|
| `http_logger` | Optional logger used by `Clowk::Http`. |
|
|
115
115
|
| `session_status_cache` | Where API-only apps cache session status. Defaults to `Rails.cache`. |
|
|
116
116
|
|
|
117
|
+
### Runtime credentials
|
|
118
|
+
|
|
119
|
+
`Clowk.configure` is the right place for credentials that are a boot constant.
|
|
120
|
+
Some apps do not have that: an operator pastes a publishable key into a settings
|
|
121
|
+
screen and expects sign-in to work on the next request, or one process serves
|
|
122
|
+
several tenants and each one has its own instance.
|
|
123
|
+
|
|
124
|
+
For those, there is one method — `Clowk.with_credentials`. It scopes the
|
|
125
|
+
credentials to a block instead of the process:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
Clowk.with_credentials(publishable_key: "pk_live_…") do
|
|
129
|
+
# sign-in URLs, JWKS, token verification and the SDK client
|
|
130
|
+
# all resolve against that instance in here
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`secret_key` and `subdomain_url` go in the same way and are both optional —
|
|
135
|
+
RS256 needs no secret, and the auth domain is resolved from the publishable key
|
|
136
|
+
when absent.
|
|
137
|
+
|
|
138
|
+
In a Rails controller, wire it with an `around_action` you name yourself:
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
class ApplicationController < ActionController::Base
|
|
142
|
+
include Clowk::Authenticable
|
|
143
|
+
|
|
144
|
+
around_action :require_tenant_key!
|
|
145
|
+
before_action :authenticate_clowk_user!
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
def require_tenant_key!(&)
|
|
150
|
+
Clowk.with_credentials(publishable_key: Tenant.current.key, &)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
How you spell the block is yours too — `(&)`, `(&block)`, `(&action)`, or
|
|
156
|
+
`{ yield }`. The examples here use `(&)`; nothing in the gem depends on it.
|
|
157
|
+
|
|
158
|
+
`:allow_own_credentials!`, `:use_workspace_sso!` — the gem does not care, and
|
|
159
|
+
never looks for a method named its way. Where the credentials come from is
|
|
160
|
+
equally yours: an environment variable, a settings row, the subdomain, a header.
|
|
161
|
+
|
|
162
|
+
`nil` runs the block against the boot configuration, so a request with no tenant
|
|
163
|
+
needs no branch at the call site:
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
def require_tenant_key!(&)
|
|
167
|
+
Clowk.with_credentials(Tenant.current&.clowk_credentials, &)
|
|
168
|
+
end
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
That second form takes a `Clowk::Credentials` object, for callers that already
|
|
172
|
+
have one — a row that knows how to describe itself, say. The keyword form builds
|
|
173
|
+
it for you, so the common case never has to name the class.
|
|
174
|
+
|
|
175
|
+
The same call works in a job, a rake task or a script; there is nothing
|
|
176
|
+
controller-specific about it, and `Clowk::Authenticable` deliberately adds no
|
|
177
|
+
second name for the same idea.
|
|
178
|
+
|
|
179
|
+
Nothing installs the filter for you. `around_action` has to wrap
|
|
180
|
+
`authenticate_clowk_user!`, and where it sits among your own filters is a
|
|
181
|
+
decision only your app can make — a gem that registers callbacks on your
|
|
182
|
+
controller is one you have to read to know what runs.
|
|
183
|
+
|
|
184
|
+
`Clowk.credentials` is the reader: the scoped value when one is in force, the
|
|
185
|
+
boot configuration otherwise. An app that never scopes anything behaves exactly
|
|
186
|
+
as it did before this existed, and explicitly passed arguments
|
|
187
|
+
(`Clowk::SDK::Client.new(secret_key: …)`) still win over both.
|
|
188
|
+
|
|
189
|
+
**There is no `Clowk.secret_key = …` setter, on purpose.** A setter has no
|
|
190
|
+
lifetime, so the first request that raises between the assignment and its reset
|
|
191
|
+
leaves that key installed process-wide — and the secret key mints HS256 tokens
|
|
192
|
+
for any subject, on the verification path that does not check `aud`. A leaked
|
|
193
|
+
scope would be an authentication bypass rather than an untidy configuration. The
|
|
194
|
+
block's `ensure` is what makes the lifetime a property of the API instead of the
|
|
195
|
+
caller's discipline. Scopes nest, and the inner one restores the outer.
|
|
196
|
+
|
|
197
|
+
Scoping is fiber-based (`ActiveSupport::IsolatedExecutionState`), so it survives
|
|
198
|
+
streaming responses and async adapters rather than reverting to the global
|
|
199
|
+
halfway through a request.
|
|
200
|
+
|
|
201
|
+
The caches were already built for this: `Clowk::Jwks` keys by URL and
|
|
202
|
+
`Clowk::Subdomain` keys by publishable key, so switching instances at runtime
|
|
203
|
+
selects a different cache entry instead of poisoning the previous one.
|
|
204
|
+
|
|
117
205
|
### API-only Rails apps
|
|
118
206
|
|
|
119
207
|
`Clowk::Authenticable` works in `ActionController::API` controllers with no
|
|
@@ -259,6 +347,27 @@ Mounted routes exposed by the engine:
|
|
|
259
347
|
|
|
260
348
|
When you mount the engine elsewhere, the same route set is exposed under your chosen prefix.
|
|
261
349
|
|
|
350
|
+
### Turbo and the sign-in redirect
|
|
351
|
+
|
|
352
|
+
`/clowk/sign_in` answers with a redirect to your Clowk instance, which is a
|
|
353
|
+
different origin. **Turbo cannot follow a cross-origin redirect.** It does not
|
|
354
|
+
raise and it does not warn: the fetch is dropped, the page stays exactly as it
|
|
355
|
+
was, and the submit reads as a button that does nothing. A later manual refresh
|
|
356
|
+
works, which makes it look like flakiness rather than a missing opt-out.
|
|
357
|
+
|
|
358
|
+
Links are fine. A **form** that ends up at `/sign_in` — a settings screen that
|
|
359
|
+
turns sign-in on, say — has to opt out so the browser performs the navigation
|
|
360
|
+
itself:
|
|
361
|
+
|
|
362
|
+
```erb
|
|
363
|
+
<%= form_with url: clowk.sign_in_path, method: :get, data: { turbo: false } do |form| %>
|
|
364
|
+
<%= form.submit 'Sign in' %>
|
|
365
|
+
<% end %>
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
The same applies to any of your own actions that redirect to `/sign_in` at the
|
|
369
|
+
end: the chain still terminates cross-origin.
|
|
370
|
+
|
|
262
371
|
## Token sources
|
|
263
372
|
|
|
264
373
|
The concern can read the token from:
|
|
@@ -405,4 +514,4 @@ Its job is to make the Rails side of Clowk integration predictable:
|
|
|
405
514
|
|
|
406
515
|
## License
|
|
407
516
|
|
|
408
|
-
|
|
517
|
+
MIT. See `LICENSE`.
|
data/clowk.gemspec
CHANGED
|
@@ -11,8 +11,8 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.summary = "Rails SDK for Clowk authentication"
|
|
12
12
|
spec.description = "Clowk Authentication, JWT verification, and future API access"
|
|
13
13
|
spec.homepage = "https://clowk.in"
|
|
14
|
-
spec.license = "
|
|
15
|
-
spec.required_ruby_version = ">= 3.
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 3.1"
|
|
16
16
|
spec.metadata = {
|
|
17
17
|
"rubygems_mfa_required" => "true"
|
|
18
18
|
}
|
data/lib/clowk/authenticable.rb
CHANGED
|
@@ -55,6 +55,26 @@ module Clowk
|
|
|
55
55
|
Clowk::Authenticable.install_dynamic_methods(self)
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
# Per-request credentials — for apps whose keys are not a boot constant:
|
|
59
|
+
# an operator pastes a publishable key into a settings screen, or one
|
|
60
|
+
# process serves several tenants.
|
|
61
|
+
#
|
|
62
|
+
# There is no method here for that, on purpose. `Clowk.with_credentials`
|
|
63
|
+
# does it, works the same in a job or a rake task, and one name is one name
|
|
64
|
+
# to remember:
|
|
65
|
+
#
|
|
66
|
+
# around_action :require_tenant_key!
|
|
67
|
+
#
|
|
68
|
+
# def require_tenant_key!(&)
|
|
69
|
+
# Clowk.with_credentials(publishable_key: Tenant.current.key, &)
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# Name that filter whatever your app calls the idea, and spell the block
|
|
73
|
+
# however you like — `(&)`, `(&block)`, `{ yield }`. Nothing in this gem
|
|
74
|
+
# looks for a method of its own naming, and no macro installs a callback
|
|
75
|
+
# for you. `around_action` has to wrap `authenticate_clowk_user!`, and
|
|
76
|
+
# where it sits among your own filters is a decision only your app can make.
|
|
77
|
+
|
|
58
78
|
def clowk_current_resource
|
|
59
79
|
@clowk_current_resource ||= begin
|
|
60
80
|
payload = stored_user_payload || verified_request_payload
|
|
@@ -241,9 +261,11 @@ module Clowk
|
|
|
241
261
|
resource = clowk_current_resource
|
|
242
262
|
|
|
243
263
|
return unless resource&.session_id
|
|
244
|
-
|
|
264
|
+
secret_key = Clowk.credentials.secret_key
|
|
265
|
+
|
|
266
|
+
return unless secret_key.present?
|
|
245
267
|
|
|
246
|
-
client = Clowk::SDK::Client.new(secret_key:
|
|
268
|
+
client = Clowk::SDK::Client.new(secret_key: secret_key)
|
|
247
269
|
result = client.tokens.verify_with_session(token: current_token)
|
|
248
270
|
status = result&.dig(:session)
|
|
249
271
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/isolated_execution_state"
|
|
4
|
+
|
|
5
|
+
module Clowk
|
|
6
|
+
# Credentials — which Clowk instance is answering, right here, right now.
|
|
7
|
+
#
|
|
8
|
+
# Everything else in Configuration describes the HOST: where the callback
|
|
9
|
+
# lives, how long HTTP waits, what the helpers are called. Those are decided
|
|
10
|
+
# once, at boot, by the person who wrote the initializer. These four are
|
|
11
|
+
# different — they name an instance, and an app that lets an operator paste a
|
|
12
|
+
# publishable key into a settings screen, or that serves several tenants from
|
|
13
|
+
# one process, has to be able to answer "which instance" per request rather
|
|
14
|
+
# than per process.
|
|
15
|
+
#
|
|
16
|
+
# A value object rather than four accessors because they only mean anything
|
|
17
|
+
# together. A publishable key from one instance beside a secret key from
|
|
18
|
+
# another is not a partial configuration, it is a broken one, and separate
|
|
19
|
+
# setters make that state reachable in between two assignments.
|
|
20
|
+
#
|
|
21
|
+
# `audience` defaults to the publishable key, which is what Clowk stamps into
|
|
22
|
+
# `aud` — the same rule Configuration#audience has always had, and it is
|
|
23
|
+
# carried here rather than left on the global for the reason this class
|
|
24
|
+
# exists: a SCOPED publishable key must be checked against a scoped audience.
|
|
25
|
+
# Reading the audience off the global while the key came from a tenant would
|
|
26
|
+
# verify one tenant's token against another tenant's expectation, silently,
|
|
27
|
+
# on the happy path. Pass `false` to switch the check off.
|
|
28
|
+
#
|
|
29
|
+
# See Clowk.with_credentials for why the scoped form is a block and not a
|
|
30
|
+
# setter.
|
|
31
|
+
# A Struct rather than a Data, and frozen by hand, so the gem keeps running on
|
|
32
|
+
# Ruby 3.1 — `Data` arrived in 3.2. The freeze is not decoration: credentials
|
|
33
|
+
# are installed into a scope and read from four places while a request runs,
|
|
34
|
+
# and a member somebody could reassign halfway through would be the
|
|
35
|
+
# half-swapped state this class exists to make unreachable.
|
|
36
|
+
Credentials = Struct.new(:publishable_key, :secret_key, :subdomain_url, :jwks_url, :audience,
|
|
37
|
+
keyword_init: true) do
|
|
38
|
+
# Every member optional: an app that only sets a publishable key is the
|
|
39
|
+
# common case, and RS256 needs nothing else.
|
|
40
|
+
#
|
|
41
|
+
# `audience` is DERIVED HERE, at construction, rather than in the reader.
|
|
42
|
+
# Deriving it on read would mean `to_h` and `==` saw nil while callers saw
|
|
43
|
+
# the publishable key — a value object that disagrees with itself. Settled
|
|
44
|
+
# once, so what it holds is what it means.
|
|
45
|
+
def initialize(publishable_key: nil, secret_key: nil, subdomain_url: nil,
|
|
46
|
+
jwks_url: nil, audience: nil)
|
|
47
|
+
super(
|
|
48
|
+
publishable_key: publishable_key,
|
|
49
|
+
secret_key: secret_key,
|
|
50
|
+
subdomain_url: subdomain_url,
|
|
51
|
+
jwks_url: jwks_url,
|
|
52
|
+
audience: audience.nil? ? publishable_key : audience
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
freeze
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# The boot configuration, as credentials. This is what `Clowk.credentials`
|
|
59
|
+
# falls back to, so an installation that configures once and never scopes
|
|
60
|
+
# anything behaves exactly as it did before this existed.
|
|
61
|
+
def self.from(config)
|
|
62
|
+
new(
|
|
63
|
+
publishable_key: config.publishable_key,
|
|
64
|
+
secret_key: config.secret_key,
|
|
65
|
+
subdomain_url: config.subdomain_url,
|
|
66
|
+
jwks_url: config.jwks_url,
|
|
67
|
+
audience: config.audience
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class << self
|
|
73
|
+
# The credentials in force for the current execution context.
|
|
74
|
+
#
|
|
75
|
+
# The scoped override if there is one, the boot configuration otherwise.
|
|
76
|
+
# Read this instead of Clowk.config wherever an instance is being named, so
|
|
77
|
+
# a consumer that never scopes anything pays nothing and a consumer that
|
|
78
|
+
# does is served correctly.
|
|
79
|
+
def credentials
|
|
80
|
+
ActiveSupport::IsolatedExecutionState[:clowk_credentials] || Credentials.from(config)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Run a block against a particular Clowk instance.
|
|
84
|
+
#
|
|
85
|
+
# THE ONLY METHOD AN APP EVER CALLS. Attributes go straight in, so the
|
|
86
|
+
# common case never has to name the Credentials class:
|
|
87
|
+
#
|
|
88
|
+
# Clowk.with_credentials(publishable_key: tenant.key) do
|
|
89
|
+
# # sign-in URLs, JWKS, verification and the API client
|
|
90
|
+
# # all resolve against that instance in here
|
|
91
|
+
# end
|
|
92
|
+
#
|
|
93
|
+
# A prebuilt object works too, for callers that already have one — an
|
|
94
|
+
# ActiveRecord row that knows how to describe itself, say:
|
|
95
|
+
#
|
|
96
|
+
# Clowk.with_credentials(tenant.clowk_credentials) { … }
|
|
97
|
+
#
|
|
98
|
+
# And nil runs the block against the boot configuration, so "this request
|
|
99
|
+
# has no tenant" needs no branch at the call site:
|
|
100
|
+
#
|
|
101
|
+
# Clowk.with_credentials(tenant&.clowk_credentials) { … }
|
|
102
|
+
#
|
|
103
|
+
# A BLOCK, AND DELIBERATELY NOT A SETTER. `Clowk.secret_key = x` has no
|
|
104
|
+
# lifetime: the first request that raises between the assignment and its
|
|
105
|
+
# reset leaves that key installed process-wide, and the secret key mints
|
|
106
|
+
# HS256 tokens for any subject — Clowk::JwtVerifier routes to the symmetric
|
|
107
|
+
# path whenever `alg` is not RS256, and that path does not check the
|
|
108
|
+
# audience. So a leaked scope is not an untidy configuration, it is an
|
|
109
|
+
# authentication bypass. The `ensure` below is the whole API.
|
|
110
|
+
#
|
|
111
|
+
# Fiber-scoped via ActiveSupport::IsolatedExecutionState rather than
|
|
112
|
+
# Thread.current, so it survives the places Rails hands work to a fiber —
|
|
113
|
+
# streaming responses, async adapters — instead of silently reverting to
|
|
114
|
+
# the global halfway through a request.
|
|
115
|
+
#
|
|
116
|
+
# Nesting restores the enclosing scope, not the global.
|
|
117
|
+
def with_credentials(credentials = nil, **attributes)
|
|
118
|
+
resolved = credentials || (Credentials.new(**attributes) unless attributes.empty?)
|
|
119
|
+
|
|
120
|
+
return yield if resolved.nil?
|
|
121
|
+
|
|
122
|
+
previous = ActiveSupport::IsolatedExecutionState[:clowk_credentials]
|
|
123
|
+
ActiveSupport::IsolatedExecutionState[:clowk_credentials] = resolved
|
|
124
|
+
|
|
125
|
+
yield
|
|
126
|
+
ensure
|
|
127
|
+
ActiveSupport::IsolatedExecutionState[:clowk_credentials] = previous if resolved
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
data/lib/clowk/jwks.rb
CHANGED
|
@@ -30,7 +30,7 @@ module Clowk
|
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def key_for(kid, jwks_url: Clowk.
|
|
33
|
+
def key_for(kid, jwks_url: Clowk.credentials.jwks_url)
|
|
34
34
|
url = jwks_url || default_url
|
|
35
35
|
|
|
36
36
|
key = lookup(url, kid)
|
|
@@ -48,7 +48,7 @@ module Clowk
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def default_url
|
|
51
|
-
base = Clowk.
|
|
51
|
+
base = Clowk.credentials.subdomain_url || Clowk::Subdomain.resolve_url!
|
|
52
52
|
|
|
53
53
|
"#{base.to_s.chomp("/")}#{WELL_KNOWN_PATH}"
|
|
54
54
|
rescue ConfigurationError
|
data/lib/clowk/jwt_verifier.rb
CHANGED
|
@@ -11,8 +11,11 @@ module Clowk
|
|
|
11
11
|
# LEGACY_ALGORITHM — this no longer describes what the verifier accepts.
|
|
12
12
|
ALGORITHM = LEGACY_ALGORITHM
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
# Credentials come from Clowk.credentials, which is the scoped override when
|
|
15
|
+
# one is in force and the boot configuration otherwise. `issuer` stays on
|
|
16
|
+
# config: it describes the token format, not which instance issued it.
|
|
17
|
+
def initialize(secret_key: Clowk.credentials.secret_key, issuer: Clowk.config.issuer,
|
|
18
|
+
audience: Clowk.credentials.audience, jwks_url: Clowk.credentials.jwks_url)
|
|
16
19
|
@secret_key = secret_key
|
|
17
20
|
@issuer = issuer
|
|
18
21
|
@audience = audience
|
data/lib/clowk/sdk/client.rb
CHANGED
|
@@ -7,8 +7,8 @@ module Clowk
|
|
|
7
7
|
class Client
|
|
8
8
|
def initialize(options = {})
|
|
9
9
|
@api_base_url = options.fetch(:api_base_url, nil).presence || Clowk.config.api_base_url
|
|
10
|
-
@secret_key = options.fetch(:secret_key, Clowk.
|
|
11
|
-
@publishable_key = options.fetch(:publishable_key, Clowk.
|
|
10
|
+
@secret_key = options.fetch(:secret_key, Clowk.credentials.secret_key)
|
|
11
|
+
@publishable_key = options.fetch(:publishable_key, Clowk.credentials.publishable_key)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def method_missing(method_name, *, **, &)
|
data/lib/clowk/subdomain.rb
CHANGED
|
@@ -47,8 +47,8 @@ module Clowk
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def initialize(options = {})
|
|
50
|
-
@publishable_key = options.fetch(:publishable_key, Clowk.
|
|
51
|
-
@subdomain_url = options.fetch(:subdomain_url, Clowk.
|
|
50
|
+
@publishable_key = options.fetch(:publishable_key, Clowk.credentials.publishable_key)
|
|
51
|
+
@subdomain_url = options.fetch(:subdomain_url, Clowk.credentials.subdomain_url)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def resolve_url!
|
data/lib/clowk/version.rb
CHANGED
data/lib/clowk.rb
CHANGED
|
@@ -29,11 +29,16 @@ module Clowk
|
|
|
29
29
|
|
|
30
30
|
def reset!
|
|
31
31
|
Subdomain.clear_cache! if defined?(Subdomain)
|
|
32
|
+
ActiveSupport::IsolatedExecutionState[:clowk_credentials] = nil
|
|
32
33
|
@config = Configuration.new
|
|
33
34
|
end
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
|
|
38
|
+
# After the module body: Credentials reopens `class << self` to add
|
|
39
|
+
# `credentials` and `with_credentials`, so the module has to exist first.
|
|
40
|
+
require_relative "clowk/credentials"
|
|
41
|
+
|
|
37
42
|
require_relative "clowk/current"
|
|
38
43
|
require_relative "clowk/http/response"
|
|
39
44
|
require_relative "clowk/http/logger_middleware"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: clowk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Clowk
|
|
@@ -107,6 +107,7 @@ files:
|
|
|
107
107
|
- lib/clowk/controllers/base_controller.rb
|
|
108
108
|
- lib/clowk/controllers/callbacks_controller.rb
|
|
109
109
|
- lib/clowk/controllers/sessions_controller.rb
|
|
110
|
+
- lib/clowk/credentials.rb
|
|
110
111
|
- lib/clowk/current.rb
|
|
111
112
|
- lib/clowk/engine.rb
|
|
112
113
|
- lib/clowk/helpers/url_helpers.rb
|
|
@@ -129,7 +130,7 @@ files:
|
|
|
129
130
|
- lib/clowk/version.rb
|
|
130
131
|
homepage: https://clowk.in
|
|
131
132
|
licenses:
|
|
132
|
-
-
|
|
133
|
+
- MIT
|
|
133
134
|
metadata:
|
|
134
135
|
rubygems_mfa_required: 'true'
|
|
135
136
|
rdoc_options: []
|
|
@@ -139,7 +140,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
139
140
|
requirements:
|
|
140
141
|
- - ">="
|
|
141
142
|
- !ruby/object:Gem::Version
|
|
142
|
-
version: '3.
|
|
143
|
+
version: '3.1'
|
|
143
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
145
|
requirements:
|
|
145
146
|
- - ">="
|