Class: Clerk::SDK
- Inherits:
-
OpenAPIClient
- Object
- OpenAPIClient
- Clerk::SDK
- Defined in:
- lib/clerk/sdk.rb
Constant Summary collapse
- JWKS_CACHE_LIFETIME =
How often (in seconds) should JWKs be refreshed
3600- @@jwks_caches =
One JWKS cache per Clerk instance. A single process-wide cache would let a token minted by one instance verify against another, since the keys it holds are whichever instance refreshed last and no issuer check runs. The cache still outlives individual SDK objects, because
authenticate_requestbuilds a fresh SDK per request. rubocop:disable Style/ClassVars {}
- @@jwks_caches_mutex =
Mutex.new
Instance Attribute Summary collapse
-
#jwks_cache_scope ⇒ Object
readonly
Opaque identifier for the Clerk instance this SDK talks to.
Class Method Summary collapse
-
.jwks_cache(scope) ⇒ Object
Returns the JWKS cache for a single Clerk instance.
Instance Method Summary collapse
-
#decode_token(token) ⇒ Object
Returns the decoded JWT payload without verifying if the signature is valid.
-
#initialize(client: nil, retry_config: nil, timeout_ms: nil, secret_key: nil, security_source: nil, server_idx: nil, server_url: nil, url_params: nil) ⇒ SDK
constructor
A new instance of SDK.
-
#verify_token(token, force_refresh_jwks: false, algorithms: ['RS256'], timeout: 5) ⇒ Object
Decode the JWT and verify it's valid (verify claims, signature etc.) using the provided algorithms.
Constructor Details
#initialize(client: nil, retry_config: nil, timeout_ms: nil, secret_key: nil, security_source: nil, server_idx: nil, server_url: nil, url_params: nil) ⇒ SDK
Returns a new instance of SDK.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/clerk/sdk.rb', line 42 def initialize(client: nil, retry_config: nil, timeout_ms: nil, secret_key: nil, security_source: nil, server_idx: nil, server_url: nil, url_params: nil) secret_key ||= Clerk.configuration.secret_key @jwks_cache_scope = Digest::SHA256.hexdigest( [server_url, server_idx, secret_key].join("\0") ) super( client: client, retry_config: retry_config, timeout_ms: timeout_ms, bearer_auth: secret_key, security_source: security_source, server_idx: server_idx, server_url: server_url, url_params: url_params ) end |
Instance Attribute Details
#jwks_cache_scope ⇒ Object (readonly)
Opaque identifier for the Clerk instance this SDK talks to. The secret key is hashed so it is never retained as a cache key.
40 41 42 |
# File 'lib/clerk/sdk.rb', line 40 def jwks_cache_scope @jwks_cache_scope end |
Class Method Details
.jwks_cache(scope) ⇒ Object
Returns the JWKS cache for a single Clerk instance. scope must identify
that instance; see #jwks_cache_scope.
32 33 34 35 36 |
# File 'lib/clerk/sdk.rb', line 32 def self.jwks_cache(scope) @@jwks_caches_mutex.synchronize do @@jwks_caches[scope] ||= JWKSCache.new(JWKS_CACHE_LIFETIME) end end |
Instance Method Details
#decode_token(token) ⇒ Object
Returns the decoded JWT payload without verifying if the signature is valid.
WARNING: This will not verify whether the signature is valid. You should not
use this for untrusted messages! You most likely want to use verify_token.
63 64 65 |
# File 'lib/clerk/sdk.rb', line 63 def decode_token(token) JWT.decode(token, nil, false).first end |
#verify_token(token, force_refresh_jwks: false, algorithms: ['RS256'], timeout: 5) ⇒ Object
Decode the JWT and verify it's valid (verify claims, signature etc.) using the provided algorithms.
JWKS are cached for JWKS_CACHE_LIFETIME seconds, in order to avoid unecessary roundtrips.
In order to invalidate the cache, pass force_refresh_jwks: true.
A timeout for the request to the JWKs endpoint can be set with the timeout argument.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/clerk/sdk.rb', line 73 def verify_token(token, force_refresh_jwks: false, algorithms: ['RS256'], timeout: 5) jwk_loader = lambda do || # JWT.decode requires that the 'keys' key in the Hash is a symbol (as # opposed to a string which our SDK returns by default) {keys: SDK.jwks_cache(@jwks_cache_scope).fetch(self, kid_not_found: [:invalidate] || [:kid_not_found], force_refresh: force_refresh_jwks)} end begin claims = JWT.decode(token, nil, true, algorithms: algorithms, exp_leeway: timeout, jwks: jwk_loader).first rescue JWT::ExpiredSignature => e raise e rescue JWT::InvalidIatError => e raise e rescue JWT::DecodeError => e raise e rescue StandardError => e raise e end # orgs if claims['v'].nil? || claims['v'] == 1 claims['v'] = 1 elsif claims['v'] == 2 && claims['o'] claims['org_id'] = claims['o'].fetch('id', nil) claims['org_slug'] = claims['o'].fetch('slg', nil) claims['org_role'] = "org:#{claims['o'].fetch('rol', nil)}" = (claims) claims['org_permissions'] = if .any? claims.delete('o') claims.delete('fea') end claims end |