clerk-sdk-ruby 1.0.0 → 2.0.0.alpha.1
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/.gitignore +2 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile.lock +15 -5
- data/README.md +19 -9
- data/bin/console +1 -0
- data/clerk-sdk-ruby.gemspec +4 -0
- data/lib/clerk/authenticatable.rb +43 -5
- data/lib/clerk/proxy.rb +0 -0
- data/lib/clerk/rack_middleware.rb +53 -26
- data/lib/clerk/rack_middleware_v2.rb +168 -0
- data/lib/clerk/railtie.rb +2 -7
- data/lib/clerk/resources/jwks.rb +18 -0
- data/lib/clerk/resources/users.rb +5 -0
- data/lib/clerk/resources.rb +1 -0
- data/lib/clerk/sdk.rb +68 -5
- data/lib/clerk/version.rb +1 -1
- data/lib/clerk.rb +5 -4
- metadata +54 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e7b69485de55997a4dac75759908e7709dcb7fdb83c92fe09d2c4748a0a74f7
|
4
|
+
data.tar.gz: a0afbe19f7d6a7a9a998e82b798a8a548f051505e14f3b1d50688d2aaa436d7f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 062e4297db4b8b20e1a6ed6bf6d69a2b0f2df0f5aecc5d716e6b883877fea484476c9d75bf7e7fda294b88774b707b60fd09a85604f7f9363b1161f622d4947a
|
7
|
+
data.tar.gz: 50ecd09031d4383b5d136840f8457841253c86122fb3a4a8769d8d86553fb3d2ff9323f0cb98bdde68f4990d2c4cc955b9996a3fca09a0c597e10a7bbb39d8eb
|
data/.gitignore
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
## unreleased
|
2
|
+
|
3
|
+
This release (v2) introduces the new networkless middleware which is compatible
|
4
|
+
with the new authentication scheme, dubbed *AuthV2*.
|
5
|
+
|
6
|
+
It is backwards-incompatible with applications using AuthV1.
|
7
|
+
|
8
|
+
- [BREAKING]: In order to use this version, you must set the authVersion prop
|
9
|
+
accordingly in your frontend: `Clerk.load({authVersion: 2})`
|
10
|
+
|
11
|
+
## 1.0.3 - 2021-07-21
|
12
|
+
|
13
|
+
- fix: Proper endpoint for oauth_access_token method
|
14
|
+
|
15
|
+
## 1.0.2 - 2021-06-03
|
16
|
+
|
17
|
+
- fix: Instantiation of `Clerk::SDK` without prior call to `Clerk.configure`
|
18
|
+
|
19
|
+
## 1.0.1 - 2021-06-03
|
20
|
+
|
21
|
+
### enhancements
|
22
|
+
|
23
|
+
- Middleware now uses a proxy object which lazy loads the Clerk session and user only when needed
|
24
|
+
|
25
|
+
## 1.0.0 - 2021-05-27
|
26
|
+
|
27
|
+
- initial release
|
data/Gemfile.lock
CHANGED
@@ -1,33 +1,43 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
clerk-sdk-ruby (1.0.
|
4
|
+
clerk-sdk-ruby (1.0.3)
|
5
5
|
faraday (~> 1.4.1)
|
6
|
+
jwt (~> 2.2)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
10
|
-
|
11
|
+
byebug (11.1.3)
|
12
|
+
faraday (1.4.3)
|
13
|
+
faraday-em_http (~> 1.0)
|
14
|
+
faraday-em_synchrony (~> 1.0)
|
11
15
|
faraday-excon (~> 1.1)
|
12
16
|
faraday-net_http (~> 1.0)
|
13
17
|
faraday-net_http_persistent (~> 1.1)
|
14
18
|
multipart-post (>= 1.2, < 3)
|
15
19
|
ruby2_keywords (>= 0.0.4)
|
20
|
+
faraday-em_http (1.0.0)
|
21
|
+
faraday-em_synchrony (1.0.0)
|
16
22
|
faraday-excon (1.1.0)
|
17
23
|
faraday-net_http (1.0.1)
|
18
|
-
faraday-net_http_persistent (1.
|
24
|
+
faraday-net_http_persistent (1.2.0)
|
25
|
+
jwt (2.2.3)
|
19
26
|
minitest (5.14.2)
|
20
27
|
multipart-post (2.1.1)
|
21
28
|
rake (13.0.3)
|
22
|
-
ruby2_keywords (0.0.
|
29
|
+
ruby2_keywords (0.0.5)
|
30
|
+
timecop (0.9.4)
|
23
31
|
|
24
32
|
PLATFORMS
|
25
33
|
x86_64-linux
|
26
34
|
|
27
35
|
DEPENDENCIES
|
36
|
+
byebug (~> 11.1)
|
28
37
|
clerk-sdk-ruby!
|
29
38
|
minitest (~> 5.0)
|
30
39
|
rake (~> 13.0)
|
40
|
+
timecop (~> 0.9.4)
|
31
41
|
|
32
42
|
BUNDLED WITH
|
33
|
-
2.2.
|
43
|
+
2.2.24
|
data/README.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Clerk Ruby SDK
|
2
2
|
|
3
|
+
**NOTE**: This is the v2 branch of the SDK, which requires that you use AuthV2
|
4
|
+
in your frontend. This means that you have to set the `authVersion` prop
|
5
|
+
accordingly in your frontend:
|
6
|
+
|
7
|
+
```javascript
|
8
|
+
Clerk.load({authVersion: 2})
|
9
|
+
```
|
10
|
+
|
11
|
+
----------
|
12
|
+
|
3
13
|
Thank you for choosing [Clerk](https://clerk.dev/) for your authentication,
|
4
14
|
session & user management needs!
|
5
15
|
|
@@ -63,10 +73,10 @@ supported configuration settings their environment variable equivalents:
|
|
63
73
|
|
64
74
|
```ruby
|
65
75
|
Clerk.configure do |c|
|
66
|
-
c.api_key = "your_api_key" # if omitted: ENV
|
76
|
+
c.api_key = "your_api_key" # if omitted: ENV["CLERK_API_KEY"] - API calls will fail if unset
|
67
77
|
c.base_url = "https://..." # if omitted: "https://api.clerk.dev/v1/"
|
68
78
|
c.logger = Logger.new(STDOUT) # if omitted, no logging
|
69
|
-
c.middleware_cache_store = ActiveSupport::Cache::FileStore.new("/tmp/clerk_middleware_cache") # if omitted:
|
79
|
+
c.middleware_cache_store = ActiveSupport::Cache::FileStore.new("/tmp/clerk_middleware_cache") # if omitted: no caching
|
70
80
|
end
|
71
81
|
```
|
72
82
|
|
@@ -94,17 +104,17 @@ for details.
|
|
94
104
|
|
95
105
|
## Rack middleware
|
96
106
|
|
97
|
-
The SDK comes with a Rack middleware which
|
98
|
-
|
99
|
-
|
100
|
-
|
107
|
+
The SDK comes with a Rack middleware which lazily loads the Clerk session and
|
108
|
+
user. It inserts a `clerk` key in the Rack environment, which is an instance
|
109
|
+
of `Clerk::Proxy`. To get the session or the user of the session, you call
|
110
|
+
`session` or `user` respectively. In case there is no session, you can retrieve
|
111
|
+
the API error with the `error` getter method.
|
101
112
|
|
102
113
|
## Rails integration
|
103
114
|
|
104
115
|
The SDK will automatically add the [Rack middleware](#rack-middleware) to the
|
105
|
-
middleware stack
|
106
|
-
|
107
|
-
controller:
|
116
|
+
middleware stack. For easier access to the Clerk session and user, include the
|
117
|
+
`Clerk::Authenticatable` concern in your controller:
|
108
118
|
|
109
119
|
```ruby
|
110
120
|
require "clerk/authenticatable"
|
data/bin/console
CHANGED
data/clerk-sdk-ruby.gemspec
CHANGED
@@ -28,4 +28,8 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.require_paths = ["lib"]
|
29
29
|
|
30
30
|
spec.add_dependency "faraday", "~> 1.4.1"
|
31
|
+
spec.add_dependency "jwt", '~> 2.2'
|
32
|
+
|
33
|
+
spec.add_development_dependency "byebug", "~> 11.1"
|
34
|
+
spec.add_development_dependency "timecop", "~> 0.9.4"
|
31
35
|
end
|
@@ -5,16 +5,52 @@ module Clerk
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
protected
|
8
|
+
|
9
|
+
# Makes a request to the Clerk API to verify the session again and return
|
10
|
+
# the Session object. Subsequent calls to this method will return the cached
|
11
|
+
# Session object.
|
12
|
+
#
|
13
|
+
# NOTE: For better performance, you can instead use `#clerk_session_claims`
|
14
|
+
# which already contains the verified claims as retrieved from the session
|
15
|
+
# token.
|
8
16
|
def clerk_session
|
9
|
-
request.env["
|
17
|
+
request.env["clerk"].session
|
18
|
+
end
|
19
|
+
|
20
|
+
# Makes a request to the Clerk API to verify the session again. Returns the
|
21
|
+
# session object as fetched from the API.
|
22
|
+
#
|
23
|
+
# NOTE: For better performance, you can instead use `#clerk_session_claims`
|
24
|
+
# which already contains the verified claims as retrieved from the session
|
25
|
+
# token.
|
26
|
+
#
|
27
|
+
# See https://docs.clerk.dev/reference/backend-api-reference/sessions#verify-a-session
|
28
|
+
def clerk_reverify_session!
|
29
|
+
request.env["clerk"].verify_session
|
30
|
+
end
|
31
|
+
|
32
|
+
def clerk_verified_session_claims
|
33
|
+
request.env["clerk"].session_claims
|
10
34
|
end
|
11
35
|
|
36
|
+
def clerk_verified_session_token
|
37
|
+
request.env["clerk"].session_token
|
38
|
+
end
|
39
|
+
|
40
|
+
# Makes a request to the Clerk API to fetch the data of the authenticated
|
41
|
+
# session's user. If caching is configured (see
|
42
|
+
# Config.middleware_cache_store), subsequent calls will return the cached
|
43
|
+
# object.
|
12
44
|
def clerk_user
|
13
|
-
request.env["
|
45
|
+
request.env["clerk"].user
|
46
|
+
end
|
47
|
+
|
48
|
+
def clerk_user_id
|
49
|
+
request.env["clerk"].user_id
|
14
50
|
end
|
15
51
|
|
16
52
|
def clerk_user_signed_in?
|
17
|
-
!!
|
53
|
+
!!clerk_verified_session_claims
|
18
54
|
end
|
19
55
|
|
20
56
|
def clerk_sign_in_url
|
@@ -30,8 +66,10 @@ module Clerk
|
|
30
66
|
end
|
31
67
|
|
32
68
|
included do
|
33
|
-
helper_method :clerk_session, :
|
34
|
-
:
|
69
|
+
helper_method :clerk_session, :clerk_reverify_session!,
|
70
|
+
:clerk_verified_session_claims, :clerk_verified_session_token,
|
71
|
+
:clerk_user, :clerk_user_id, :clerk_user_signed_in?, :clerk_sign_in_url,
|
72
|
+
:clerk_sign_up_url, :clerk_user_profile_url
|
35
73
|
end
|
36
74
|
end
|
37
75
|
end
|
data/lib/clerk/proxy.rb
ADDED
File without changes
|
@@ -8,48 +8,75 @@ module Clerk
|
|
8
8
|
|
9
9
|
def call(env)
|
10
10
|
req = Rack::Request.new(env)
|
11
|
-
|
11
|
+
env["clerk"] = Proxy.new(env)
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
end
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
class Proxy
|
17
|
+
attr_reader :session_id, :error
|
18
|
+
def initialize(env)
|
19
|
+
req = Rack::Request.new(env)
|
20
|
+
@token = req.cookies["__session"]
|
21
|
+
@session_id = req.params["_clerk_session_id"]
|
22
|
+
@session = nil
|
23
|
+
@user_id = nil
|
24
|
+
@user = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def session
|
28
|
+
return nil if @token.nil?
|
29
|
+
return @session if @session
|
30
|
+
|
31
|
+
begin
|
32
|
+
@session = fetch_session
|
33
|
+
rescue Errors::Authentication => e
|
34
|
+
@error = e
|
23
35
|
end
|
24
|
-
@
|
36
|
+
@session
|
37
|
+
end
|
38
|
+
|
39
|
+
def user_id
|
40
|
+
@user_id ||= session.dig("user_id")
|
41
|
+
end
|
42
|
+
|
43
|
+
def user
|
44
|
+
return nil if session.nil?
|
45
|
+
@user ||= fetch_user(user_id)
|
46
|
+
end
|
47
|
+
|
48
|
+
def debug
|
49
|
+
(instance_variables - [:@sdk]).map do |ivar|
|
50
|
+
[ivar.to_s, instance_variable_get(ivar)]
|
51
|
+
end.to_h
|
25
52
|
end
|
26
53
|
|
27
54
|
private
|
28
|
-
def
|
29
|
-
SDK.new
|
55
|
+
def sdk
|
56
|
+
@sdk ||= SDK.new
|
57
|
+
end
|
58
|
+
|
59
|
+
def cache_key
|
60
|
+
@cache_key ||= @token.split(".")[1]
|
30
61
|
end
|
31
62
|
|
32
|
-
def fetch_session
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
sdk = clerk_sdk
|
37
|
-
sdk.sessions.verify_token(sess_id, token)
|
63
|
+
def fetch_session
|
64
|
+
if session_id
|
65
|
+
cached_fetch("clerk_session:#{session_id}:#{cache_key}") do
|
66
|
+
sdk.sessions.verify_token(session_id, @token)
|
38
67
|
end
|
39
68
|
else
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
sess_id = client["last_active_session_id"]
|
69
|
+
cached_fetch("clerk_session:#{cache_key}") do
|
70
|
+
client = sdk.clients.verify_token(@token)
|
71
|
+
@session_id = client["last_active_session_id"]
|
44
72
|
client["sessions"].find do |sess|
|
45
|
-
sess["id"] ==
|
73
|
+
sess["id"] == @session_id
|
46
74
|
end
|
47
75
|
end
|
48
76
|
end
|
49
77
|
end
|
50
78
|
|
51
79
|
def fetch_user(user_id)
|
52
|
-
sdk = clerk_sdk
|
53
80
|
cached_fetch("clerk_user:#{user_id}") do
|
54
81
|
sdk.users.find(user_id)
|
55
82
|
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require "clerk"
|
2
|
+
|
3
|
+
module Clerk
|
4
|
+
class RackMiddlewareV2
|
5
|
+
class ProxyV2
|
6
|
+
CACHE_TTL = 60 # seconds
|
7
|
+
|
8
|
+
attr_reader :session_claims, :session_token
|
9
|
+
|
10
|
+
def initialize(session_claims: nil, session_token: nil)
|
11
|
+
@session_claims = session_claims
|
12
|
+
@session_token = session_token
|
13
|
+
@session = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def session
|
17
|
+
return nil if @session_claims.nil?
|
18
|
+
|
19
|
+
@session ||= verify_session
|
20
|
+
end
|
21
|
+
|
22
|
+
def verify_session
|
23
|
+
return nil if @session_claims.nil?
|
24
|
+
|
25
|
+
sdk.sessions.verify_token(@session_claims["sid"], @session_token)
|
26
|
+
end
|
27
|
+
|
28
|
+
def user
|
29
|
+
return nil if user_id.nil?
|
30
|
+
|
31
|
+
@user ||= fetch_user(user_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def user_id
|
35
|
+
return nil if @session_claims.nil?
|
36
|
+
|
37
|
+
@session_claims["sub"]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def fetch_user(user_id)
|
43
|
+
cached_fetch("clerk_user:#{user_id}") do
|
44
|
+
sdk.users.find(user_id)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def cached_fetch(key, &block)
|
49
|
+
if store = Clerk.configuration.middleware_cache_store
|
50
|
+
store.fetch(key, expires_in: CACHE_TTL, &block)
|
51
|
+
else
|
52
|
+
yield
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def sdk
|
57
|
+
@sdk ||= Clerk::SDK.new
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(app)
|
62
|
+
@app = app
|
63
|
+
end
|
64
|
+
|
65
|
+
def call(env)
|
66
|
+
@env = env
|
67
|
+
@req = Rack::Request.new(env)
|
68
|
+
@env["clerk"] = ProxyV2.new
|
69
|
+
@header_token = @req.env["HTTP_AUTHORIZATION"]&.strip
|
70
|
+
@cookie_token = @req.cookies["__session"]
|
71
|
+
@client_uat = @req.cookies["__client_uat"]
|
72
|
+
|
73
|
+
##########################################################################
|
74
|
+
# #
|
75
|
+
# HEADER AUTHENTICATION #
|
76
|
+
# #
|
77
|
+
##########################################################################
|
78
|
+
if @header_token
|
79
|
+
return signed_out if !sdk.decode_token(@header_token) # malformed JWT
|
80
|
+
|
81
|
+
token = verify_token(@header_token)
|
82
|
+
return signed_in(token, @header_token) if token
|
83
|
+
|
84
|
+
# Clerk.js should refresh the token and retry
|
85
|
+
return unknown(interstitial: false)
|
86
|
+
end
|
87
|
+
|
88
|
+
# in cross-origin XHRs the use of Authorization header is mandatory.
|
89
|
+
if cross_origin_request?(@req) && @header_token.nil?
|
90
|
+
return signed_out
|
91
|
+
end
|
92
|
+
|
93
|
+
##########################################################################
|
94
|
+
# #
|
95
|
+
# COOKIE AUTHENTICATION #
|
96
|
+
# #
|
97
|
+
##########################################################################
|
98
|
+
if development_or_staging? && (@req.referrer.nil? || cross_origin_request?(@req))
|
99
|
+
return unknown(interstitial: true)
|
100
|
+
end
|
101
|
+
|
102
|
+
if production? && @client_uat.nil?
|
103
|
+
return signed_out
|
104
|
+
end
|
105
|
+
|
106
|
+
if @client_uat == "0"
|
107
|
+
return signed_out
|
108
|
+
end
|
109
|
+
|
110
|
+
token = verify_token(@cookie_token)
|
111
|
+
|
112
|
+
if token && token["iat"] && @client_uat && Integer(@client_uat) <= token["iat"]
|
113
|
+
return signed_in(token, @cookie_token)
|
114
|
+
end
|
115
|
+
|
116
|
+
unknown(interstitial: true)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# Outcome A
|
122
|
+
def signed_in(claims, token)
|
123
|
+
@env["clerk"] = ProxyV2.new(session_claims: claims, session_token: token)
|
124
|
+
|
125
|
+
@app.call(@env)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Outcome B
|
129
|
+
def signed_out
|
130
|
+
@app.call(@env)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Outcome C
|
134
|
+
def unknown(interstitial: false)
|
135
|
+
return [401, {}, []] if !interstitial
|
136
|
+
|
137
|
+
# Load Clerk.js to update the __session and __client_uat cookies.
|
138
|
+
[401, {"Content-Type" => "text/html"}, [sdk.interstitial]]
|
139
|
+
end
|
140
|
+
|
141
|
+
def development_or_staging?
|
142
|
+
Clerk.configuration.api_key.start_with?("test_")
|
143
|
+
end
|
144
|
+
|
145
|
+
def production?
|
146
|
+
!development_or_staging?
|
147
|
+
end
|
148
|
+
|
149
|
+
def cross_origin_request?(req)
|
150
|
+
origin = req.env["HTTP_ORIGIN"]
|
151
|
+
origin && origin != req.host
|
152
|
+
end
|
153
|
+
|
154
|
+
def verify_token(token)
|
155
|
+
return false if token.nil? || token.strip.empty?
|
156
|
+
|
157
|
+
begin
|
158
|
+
@session = sdk.verify_token(token)
|
159
|
+
rescue JWT::DecodeError, JWT::RequiredDependencyError => e
|
160
|
+
false
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def sdk
|
165
|
+
@sdk ||= Clerk::SDK.new
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/lib/clerk/railtie.rb
CHANGED
@@ -1,17 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
#
|
3
3
|
require_relative "rack_middleware"
|
4
|
+
require_relative "rack_middleware_v2"
|
4
5
|
|
5
6
|
module Clerk
|
6
7
|
class Railtie < ::Rails::Railtie
|
7
8
|
initializer "clerk_railtie.configure_rails_initialization" do |app|
|
8
|
-
app.middleware.use Clerk::
|
9
|
-
end
|
10
|
-
|
11
|
-
config.to_prepare do
|
12
|
-
Clerk.configure do |c|
|
13
|
-
c.middleware_cache_store ||= Rails.cache
|
14
|
-
end
|
9
|
+
app.middleware.use Clerk::RackMiddlewareV2
|
15
10
|
end
|
16
11
|
end
|
17
12
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require_relative "plural_resource"
|
3
|
+
|
4
|
+
module Clerk
|
5
|
+
module Resources
|
6
|
+
class JWKS
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def initialize(client)
|
10
|
+
@client = client
|
11
|
+
end
|
12
|
+
|
13
|
+
def all(timeout: 5)
|
14
|
+
@client.request(:get, "jwks", timeout: timeout)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -7,10 +7,15 @@ module Clerk
|
|
7
7
|
extend Forwardable
|
8
8
|
|
9
9
|
def initialize(client)
|
10
|
+
@client = client
|
10
11
|
@resource = PluralResource.new(client, "users")
|
11
12
|
end
|
12
13
|
|
13
14
|
def_delegators :@resource, :all, :find, :update, :delete
|
15
|
+
|
16
|
+
def oauth_access_token(user_id, provider)
|
17
|
+
@client.request(:get, "#{@resource.resource_path(user_id)}/oauth_access_tokens/#{provider}")
|
18
|
+
end
|
14
19
|
end
|
15
20
|
end
|
16
21
|
end
|
data/lib/clerk/resources.rb
CHANGED
data/lib/clerk/sdk.rb
CHANGED
@@ -4,6 +4,7 @@ require "faraday"
|
|
4
4
|
require "logger"
|
5
5
|
require "net/http"
|
6
6
|
require "json"
|
7
|
+
require "jwt"
|
7
8
|
|
8
9
|
require_relative "resources/allowlist_identifiers"
|
9
10
|
require_relative "resources/allowlist"
|
@@ -12,6 +13,8 @@ require_relative "resources/emails"
|
|
12
13
|
require_relative "resources/sessions"
|
13
14
|
require_relative "resources/sms_messages"
|
14
15
|
require_relative "resources/users"
|
16
|
+
require_relative "resources/users"
|
17
|
+
require_relative "resources/jwks"
|
15
18
|
require_relative "errors"
|
16
19
|
|
17
20
|
module Clerk
|
@@ -20,8 +23,13 @@ module Clerk
|
|
20
23
|
"User-Agent" => "Clerk/#{Clerk::VERSION}; Faraday/#{Faraday::VERSION}; Ruby/#{RUBY_VERSION}"
|
21
24
|
}
|
22
25
|
|
26
|
+
# How often (in seconds) should JWKs be refreshed
|
27
|
+
JWKS_CACHE_LIFETIME = 3600 # 1 hour
|
28
|
+
|
23
29
|
def initialize(api_key: nil, base_url: nil, logger: nil, ssl_verify: true,
|
24
30
|
connection: nil)
|
31
|
+
@jwks_fetched_at = nil
|
32
|
+
|
25
33
|
if connection # Inject a Faraday::Connection for testing or full control over Faraday
|
26
34
|
@conn = connection
|
27
35
|
return
|
@@ -48,16 +56,26 @@ module Clerk
|
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
51
|
-
def request(method, path, query: [], body: nil)
|
59
|
+
def request(method, path, query: [], body: nil, timeout: nil)
|
52
60
|
response = case method
|
53
61
|
when :get
|
54
|
-
@conn.get(path, query)
|
62
|
+
@conn.get(path, query) do |req|
|
63
|
+
req.options.timeout = timeout if timeout
|
64
|
+
end
|
55
65
|
when :post
|
56
|
-
@conn.post(path, body)
|
66
|
+
@conn.post(path, body) do |req|
|
67
|
+
req.body = body
|
68
|
+
req.options.timeout = timeout if timeout
|
69
|
+
end
|
57
70
|
when :patch
|
58
|
-
@conn.patch(path, body)
|
71
|
+
@conn.patch(path, body) do |req|
|
72
|
+
req.body = body
|
73
|
+
req.options.timeout = timeout if timeout
|
74
|
+
end
|
59
75
|
when :delete
|
60
|
-
@conn.delete(path)
|
76
|
+
@conn.delete(path) do |req|
|
77
|
+
req.options.timeout = timeout if timeout
|
78
|
+
end
|
61
79
|
end
|
62
80
|
|
63
81
|
body = if response["Content-Type"] == "application/json"
|
@@ -106,5 +124,50 @@ module Clerk
|
|
106
124
|
def users
|
107
125
|
Resources::Users.new(self)
|
108
126
|
end
|
127
|
+
|
128
|
+
def jwks
|
129
|
+
Resources::JWKS.new(self)
|
130
|
+
end
|
131
|
+
|
132
|
+
def interstitial(refresh=false)
|
133
|
+
request(:get, "internal/interstitial")
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns the decoded JWT payload without verifying if the signature is
|
137
|
+
# valid.
|
138
|
+
#
|
139
|
+
# WARNING: This will not verify whether the signature is valid. You
|
140
|
+
# should not use this for untrusted messages! You most likely want to use
|
141
|
+
# verify_token.
|
142
|
+
def decode_token(token)
|
143
|
+
JWT.decode(token, nil, false).first
|
144
|
+
end
|
145
|
+
|
146
|
+
# Decode the JWT and verify it's valid (verify claims, signature etc.) using
|
147
|
+
# the provided algorithms.
|
148
|
+
#
|
149
|
+
# JWKS are cached for JWKS_CACHE_LIFETIME seconds, in order to avoid
|
150
|
+
# unecessary roundtrips. In order to invalidate the cache, pass
|
151
|
+
# `force_refresh_jwks: true`.
|
152
|
+
#
|
153
|
+
# A timeout for the request to the JWKs endpoint can be set with the
|
154
|
+
# `timeout` argument.
|
155
|
+
def verify_token(token, force_refresh_jwks: false, algorithms: ['RS256'], timeout: 5)
|
156
|
+
jwk_loader = ->(options) do
|
157
|
+
@cached_jwks = nil if options[:invalidate] || force_refresh_jwks
|
158
|
+
@cached_jwks = nil if @jwks_fetched_at && Time.now.to_i - @jwks_fetched_at > JWKS_CACHE_LIFETIME
|
159
|
+
|
160
|
+
@cached_jwks ||= begin
|
161
|
+
keys = jwks.all["keys"]
|
162
|
+
@jwks_fetched_at = Time.now.to_i
|
163
|
+
|
164
|
+
# JWT.decode requires that the 'keys' key in the Hash is a symbol (as
|
165
|
+
# opposed to a string which our SDK returns by default)
|
166
|
+
{ keys: keys }
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
JWT.decode(token, nil, true, algorithms: algorithms, jwks: jwk_loader).first
|
171
|
+
end
|
109
172
|
end
|
110
173
|
end
|
data/lib/clerk/version.rb
CHANGED
data/lib/clerk.rb
CHANGED
@@ -5,12 +5,13 @@ require_relative "clerk/sdk"
|
|
5
5
|
|
6
6
|
module Clerk
|
7
7
|
class << self
|
8
|
-
attr_accessor :configuration
|
9
|
-
|
10
8
|
def configure
|
11
|
-
self.configuration ||= Config.new
|
12
9
|
yield(configuration)
|
13
10
|
end
|
11
|
+
|
12
|
+
def configuration
|
13
|
+
@configuration ||= Config.new
|
14
|
+
end
|
14
15
|
end
|
15
16
|
|
16
17
|
class Config
|
@@ -19,7 +20,7 @@ module Clerk
|
|
19
20
|
|
20
21
|
def initialize
|
21
22
|
@base_url = ENV.fetch("CLERK_API_BASE", PRODUCTION_BASE_URL)
|
22
|
-
@api_key = ENV
|
23
|
+
@api_key = ENV["CLERK_API_KEY"]
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clerk-sdk-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0.alpha.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Clerk
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -24,6 +24,48 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.4.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '11.1'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '11.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: timecop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.4
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.9.4
|
27
69
|
description: Client SDK for the Clerk backend API.
|
28
70
|
email:
|
29
71
|
- ruby-sdk@clerk.dev
|
@@ -33,6 +75,7 @@ extra_rdoc_files: []
|
|
33
75
|
files:
|
34
76
|
- ".github/workflows/main.yml"
|
35
77
|
- ".gitignore"
|
78
|
+
- CHANGELOG.md
|
36
79
|
- Gemfile
|
37
80
|
- Gemfile.lock
|
38
81
|
- LICENSE.txt
|
@@ -44,13 +87,16 @@ files:
|
|
44
87
|
- lib/clerk.rb
|
45
88
|
- lib/clerk/authenticatable.rb
|
46
89
|
- lib/clerk/errors.rb
|
90
|
+
- lib/clerk/proxy.rb
|
47
91
|
- lib/clerk/rack_middleware.rb
|
92
|
+
- lib/clerk/rack_middleware_v2.rb
|
48
93
|
- lib/clerk/railtie.rb
|
49
94
|
- lib/clerk/resources.rb
|
50
95
|
- lib/clerk/resources/allowlist.rb
|
51
96
|
- lib/clerk/resources/allowlist_identifiers.rb
|
52
97
|
- lib/clerk/resources/clients.rb
|
53
98
|
- lib/clerk/resources/emails.rb
|
99
|
+
- lib/clerk/resources/jwks.rb
|
54
100
|
- lib/clerk/resources/plural_resource.rb
|
55
101
|
- lib/clerk/resources/sessions.rb
|
56
102
|
- lib/clerk/resources/singular_resource.rb
|
@@ -66,7 +112,7 @@ metadata:
|
|
66
112
|
homepage_uri: https://github.com/clerkinc/clerk-sdk-ruby
|
67
113
|
source_code_uri: https://github.com/clerkinc/clerk-sdk-ruby
|
68
114
|
changelog_uri: https://github.com/clerkinc/clerk-sdk-ruby/blob/main/CHANGELOG.md
|
69
|
-
post_install_message:
|
115
|
+
post_install_message:
|
70
116
|
rdoc_options: []
|
71
117
|
require_paths:
|
72
118
|
- lib
|
@@ -77,12 +123,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
77
123
|
version: 2.4.0
|
78
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
125
|
requirements:
|
80
|
-
- - "
|
126
|
+
- - ">"
|
81
127
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
128
|
+
version: 1.3.1
|
83
129
|
requirements: []
|
84
|
-
rubygems_version: 3.2.
|
85
|
-
signing_key:
|
130
|
+
rubygems_version: 3.2.5
|
131
|
+
signing_key:
|
86
132
|
specification_version: 4
|
87
133
|
summary: Clerk SDK for Ruby.
|
88
134
|
test_files: []
|