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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f285e5ca6280db100eed30796279e412901f157a3fd43d16a41a64d9c0cd85f
4
- data.tar.gz: 32d0fab6c78b57c0f06fa9d3ef077f077cb6533ada6a400f98bec6a087d939e8
3
+ metadata.gz: 1e7b69485de55997a4dac75759908e7709dcb7fdb83c92fe09d2c4748a0a74f7
4
+ data.tar.gz: a0afbe19f7d6a7a9a998e82b798a8a548f051505e14f3b1d50688d2aaa436d7f
5
5
  SHA512:
6
- metadata.gz: 7721083974266f6b18c6ceb1e1a14abc623caad474a7fcf7a065aafa4ac51c11f732f12db257fa46e3f903a4c9985eb0e6c21e451d3ffc877b7f434ba482fe91
7
- data.tar.gz: b6a5689badfc699d7d03accb5a301a3504adf8eae009ef450b462fee9a20f995c2ec255ec9b771175fd76209ba991544a51a9e8cd6f0a14f82d6342a922942fb
6
+ metadata.gz: 062e4297db4b8b20e1a6ed6bf6d69a2b0f2df0f5aecc5d716e6b883877fea484476c9d75bf7e7fda294b88774b707b60fd09a85604f7f9363b1161f622d4947a
7
+ data.tar.gz: 50ecd09031d4383b5d136840f8457841253c86122fb3a4a8769d8d86553fb3d2ff9323f0cb98bdde68f4990d2c4cc955b9996a3fca09a0c597e10a7bbb39d8eb
data/.gitignore CHANGED
@@ -6,3 +6,5 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+
10
+ .byebug_history
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.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
- faraday (1.4.1)
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.1.0)
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.4)
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.15
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.fetch("CLERK_API_KEY") - will fail if unset
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: Rails.cache or no caching (if not in a Rails app)
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 sets the Clerk session and user in
98
- the Rack environment. The keys are: `clerk_session` and `clerk_user` for the
99
- session and user respectively. If the API responds with an error `clerk_error`
100
- will be set.
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, using `Rails.cache` for its cache. For easier access to the
106
- Clerk session and user, include the `Clerk::Authenticatable` concern in your
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
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "bundler/setup"
5
5
  require "clerk"
6
+ require "byebug"
6
7
 
7
8
  # You can add fixtures and/or initialization code here to make experimenting
8
9
  # with your gem easier. You can also use a different console, if you like.
@@ -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["clerk_session"]
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["clerk_user"]
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
- !!clerk_user
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, :clerk_user, :clerk_user_signed_in?,
34
- :clerk_sign_in_url, :clerk_sign_up_url, :clerk_user_profile_url
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
File without changes
@@ -8,48 +8,75 @@ module Clerk
8
8
 
9
9
  def call(env)
10
10
  req = Rack::Request.new(env)
11
- token = req.cookies["__session"]
11
+ env["clerk"] = Proxy.new(env)
12
+ @app.call(env)
13
+ end
14
+ end
12
15
 
13
- if token
14
- sess_id = req.params["_clerk_session_id"]
15
- begin
16
- env["clerk_session"] = fetch_session(token, sess_id)
17
- rescue Errors::Authentication => e
18
- env["clerk_error"] = e
19
- end
20
- end
21
- if sess = env["clerk_session"]
22
- env["clerk_user"] = fetch_user(sess["user_id"])
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
- @app.call(env)
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 clerk_sdk
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(token, sess_id)
33
- cache_key = token.split(".")[1]
34
- if sess_id
35
- session = cached_fetch("clerk_session:#{sess_id}:#{cache_key}") do
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
- session = cached_fetch("clerk_session:#{cache_key}") do
41
- sdk = clerk_sdk
42
- client = sdk.clients.verify_token(token)
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"] == 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::RackMiddleware
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
@@ -5,3 +5,4 @@ require_relative "resources/emails"
5
5
  require_relative "resources/sessions"
6
6
  require_relative "resources/sms_messages"
7
7
  require_relative "resources/users"
8
+ require_relative "resources/jwks"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Clerk
4
- VERSION = "1.0.0"
4
+ VERSION = "2.0.0.alpha.1"
5
5
  end
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.fetch("CLERK_API_KEY")
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: 1.0.0
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-05-27 00:00:00.000000000 Z
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: '0'
128
+ version: 1.3.1
83
129
  requirements: []
84
- rubygems_version: 3.2.15
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: []