Module: KindeSdk

Defined in:
lib/kinde_sdk.rb,
lib/kinde_sdk/client.rb,
lib/kinde_sdk/engine.rb,
lib/kinde_sdk/errors.rb,
lib/kinde_sdk/current.rb,
lib/kinde_sdk/version.rb,
lib/kinde_sdk/middleware.rb,
lib/kinde_sdk/token_store.rb,
lib/kinde_sdk/client/roles.rb,
lib/kinde_sdk/configuration.rb,
lib/kinde_sdk/token_manager.rb,
lib/kinde_sdk/client/permissions.rb,
lib/kinde_sdk/client/entitlements.rb,
lib/kinde_sdk/client/feature_flags.rb,
lib/kinde_sdk/internal/frontend_client.rb,
app/controllers/kinde_sdk/auth_controller.rb

Defined Under Namespace

Modules: Internal, PortalPage Classes: APIError, AuthController, AuthenticationError, AuthorizationError, Client, Configuration, Current, Engine, Error, Middleware, RateLimitError, TokenManager, TokenStore

Constant Summary collapse

VERSION =
"1.7.0"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.configObject

Returns the value of attribute config.



27
28
29
# File 'lib/kinde_sdk.rb', line 27

def config
  @config
end

Class Method Details

.api_client(bearer_token) ⇒ KindeApi::ApiClient

init sdk api client by bearer token

Returns:

  • (KindeApi::ApiClient)


186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/kinde_sdk.rb', line 186

def api_client(bearer_token)
  config = KindeApi::Configuration.new  # Create a new instance instead of using default
  config.configure do |c|
    c.access_token = bearer_token
    c.host = URI.parse(@config.domain).host
    c.scheme = url_scheme(c.scheme)
    c.base_path = ''  # Set empty base path since we're using the root
    c.debugging = @config.debugging
    c.logger = @config.logger
    # Set server configuration
    c.server_index = nil  # Force direct URL construction
    c.server_variables = { 'subdomain' => URI.parse(@config.domain).host.split('.').first }
  end
  KindeApi::ApiClient.new(config)
end

.auth_url(client_id: @config.client_id, client_secret: @config.client_secret, domain: @config.domain, redirect_uri: @config.callback_url, **kwargs) ⇒ Hash

receive url for authorization in Kinde itself

Returns:

  • (Hash)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/kinde_sdk.rb', line 46

def auth_url(
  client_id: @config.client_id,
  client_secret: @config.client_secret,
  domain: @config.domain,
  redirect_uri: @config.callback_url,
   **kwargs)
  params = {
    redirect_uri: redirect_uri,
    state: SecureRandom.hex,
    scope: @config.scope,
    supports_reauth: "true"
  }.merge(**kwargs)
  return { url: @config.oauth_client(
    client_id: client_id,
    client_secret: client_secret,
    domain: domain,
    authorize_url: "#{domain}/oauth2/auth",
    token_url: "#{domain}/oauth2/token").auth_code.authorize_url(params) } unless @config.pkce_enabled

  pkce_challenge = PkceChallenge.challenge(char_length: 128)
  params.merge!(code_challenge_method: 'S256', code_challenge: pkce_challenge.code_challenge)
  {
    url: @config.oauth_client(
      client_id: client_id,
      client_secret: client_secret,
      domain: domain,
      authorize_url: "#{domain}/oauth2/auth",
      token_url: "#{domain}/oauth2/token").auth_code.authorize_url(params),
    code_verifier: pkce_challenge.code_verifier
  }
end

.client(tokens_hash, auto_refresh_tokens = @config.auto_refresh_tokens, force_api = @config.force_api) ⇒ KindeSdk::Client

tokens_hash #=>

"expires_in"=>86399,
"id_token"=>"eyJhbGciOiJSUz",
"refresh_token"=>"eyJhbGciOiJSUz",
"scope"=>"openid offline email profile",
"token_type"=>"bearer"

Returns:



120
121
122
123
# File 'lib/kinde_sdk.rb', line 120

def client(tokens_hash, auto_refresh_tokens = @config.auto_refresh_tokens, force_api = @config.force_api)
  sdk_api_client = api_client(tokens_hash[:access_token] || tokens_hash["access_token"])
  KindeSdk::Client.new(sdk_api_client, tokens_hash, auto_refresh_tokens, force_api)
end

.client_credentials_access(client_id: @config.client_id, client_secret: @config.client_secret, audience: "#{@config.domain}/api", domain: @config.domain) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/kinde_sdk.rb', line 131

def client_credentials_access(
  client_id: @config.client_id,
  client_secret: @config.client_secret,
  audience: "#{@config.domain}/api",
  domain: @config.domain
)
  Faraday.new(url: domain) do |faraday|
    faraday.response :json
    faraday.use Faraday::FollowRedirects::Middleware
  end
    .post("#{domain}/oauth2/token") do |req|
    req.headers[:content_type] = 'application/x-www-form-urlencoded'
    req.body =
      "grant_type=client_credentials&client_id=#{client_id}&client_secret=#{client_secret}&audience=#{audience}"
  end.body
end

.configureObject



33
34
35
36
37
38
39
40
41
# File 'lib/kinde_sdk.rb', line 33

def configure
  if block_given?
    yield(Configuration.default)
  else
    Configuration.default
  end

  @config = Configuration.default
end

.fetch_tokens(params_or_code, client_id: @config.client_id, client_secret: @config.client_secret, domain: @config.domain, code_verifier: nil, redirect_uri: @config.callback_url) ⇒ Hash

when callback processor receives code, it needs to be used for fetching bearer token

Returns:

  • (Hash)


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
108
109
# File 'lib/kinde_sdk.rb', line 81

def fetch_tokens(
  params_or_code, 
  client_id: @config.client_id,
  client_secret: @config.client_secret,
  domain: @config.domain,
  code_verifier: nil,
  redirect_uri: @config.callback_url)
  code = params_or_code.kind_of?(Hash) ? params_or_code.fetch("code") : params_or_code
  params = {
    redirect_uri: redirect_uri,
    headers: { 'User-Agent' => "Kinde-SDK: Ruby/#{KindeSdk::VERSION}" }
  }
  params[:code_verifier] = code_verifier if code_verifier
  token = @config.oauth_client(
    client_id: client_id,
    client_secret: client_secret,
    domain: domain,
    authorize_url: "#{domain}/oauth2/auth",
    token_url: "#{domain}/oauth2/token").auth_code.get_token(code.to_s, params)

  {
    access_token: token.token,           # The access token
    id_token: token.params['id_token'],  # The ID token from params
    expires_at: token.expires_at,        # Optional: expiration time
    refresh_token: token.refresh_token,   # Optional: if present
    scope: token.params['scope'],        # The scopes requested
    token_type: token.params['token_type'] # The token type
  }.compact
end

.logout_url(logout_url: @config.logout_url, domain: @config.domain) ⇒ Object



125
126
127
128
129
# File 'lib/kinde_sdk.rb', line 125

def logout_url(logout_url: @config.logout_url, domain: @config.domain)
  query = logout_url ? URI.encode_www_form(redirect: logout_url) : nil
  host = URI::parse(domain).host
  URI::HTTP.build(host: host, path: '/logout', query: query).to_s
end

.refresh_token(hash, client_id: @config.client_id, client_secret: @config.client_secret, audience: "#{@config.domain}/api", domain: @config.domain) ⇒ Hash

Returns:

  • (Hash)


169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/kinde_sdk.rb', line 169

def refresh_token(hash,
  client_id: @config.client_id,
  client_secret: @config.client_secret,
  audience: "#{@config.domain}/api",
  domain: @config.domain
)
  OAuth2::AccessToken.from_hash(@config.oauth_client(
    client_id: client_id, 
    client_secret: client_secret,
    domain: domain,
    authorize_url: "#{domain}/oauth2/auth",
    token_url: "#{domain}/oauth2/token"), hash).refresh.to_hash
end

.token_expired?(hash, client_id: @config.client_id, client_secret: @config.client_secret, audience: "#{@config.domain}/api", domain: @config.domain) ⇒ Boolean

Returns:

  • (Boolean)


148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/kinde_sdk.rb', line 148

def token_expired?(hash,
  client_id: @config.client_id,
  client_secret: @config.client_secret,
  audience: "#{@config.domain}/api",
  domain: @config.domain
)
  begin
    validate_jwt_token(hash)
    OAuth2::AccessToken.from_hash(@config.oauth_client(
      client_id: client_id, 
      client_secret: client_secret,
      domain: domain,
      authorize_url: "#{domain}/oauth2/auth",
      token_url: "#{domain}/oauth2/token"), hash).expired?
  rescue JWT::DecodeError, OAuth2::Error => e
    Rails.logger.error("Error checking token expiration: #{e.message}")
    true
  end
end

.validate_jwt_token(token_hash) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
# File 'lib/kinde_sdk.rb', line 202

def validate_jwt_token(token_hash)
  token_hash.each do |key, token|
    next unless %w[access_token id_token].include?(key.to_s.downcase)
    begin
      jwt_validation(token, "#{@config.domain}#{@config.jwks_url}", @config.expected_issuer, @config.expected_audience)
    rescue JWT::DecodeError
      Rails.logger.error("Invalid JWT token: #{key}")
      raise JWT::DecodeError, "Invalid #{key.to_s.capitalize.gsub('_', ' ')}"
    end
  end
end