Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions app/controllers/kinde_sdk/auth_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,17 @@ class AuthController < ActionController::Base
def auth
# Generate a secure random nonce for CSRF protection
nonce = SecureRandom.urlsafe_base64(16)

# Get authorization URL and PKCE code verifier from SDK
auth_data = KindeSdk.auth_url(nonce: nonce)
auth_params = { nonce: nonce }

# Check for invitation_code in query parameters
# Validate to prevent injection and ensure it's a reasonable length
if params[:invitation_code].is_a?(String) && params[:invitation_code].strip.length > 0 && params[:invitation_code].length <= 255
auth_params[:invitation_code] = params[:invitation_code].strip
end

auth_data = KindeSdk.auth_url(**auth_params)

# Store PKCE code verifier and nonce in session for validation
session[:code_verifier] = auth_data[:code_verifier] if auth_data[:code_verifier].present?
Expand Down
7 changes: 7 additions & 0 deletions lib/kinde_sdk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ def auth_url(
scope: @config.scope,
supports_reauth: "true"
}.merge(**kwargs)

if params[:invitation_code].is_a?(String) && !params[:invitation_code].strip.empty?
params[:invitation_code] = params[:invitation_code].strip
params[:is_invitation] = "true"
else
params.delete(:invitation_code)
end
return { url: @config.oauth_client(
client_id: client_id,
client_secret: client_secret,
Expand Down
29 changes: 0 additions & 29 deletions spec/examples.txt

This file was deleted.

30 changes: 30 additions & 0 deletions spec/kinde_sdk_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,36 @@ class TestApplication < Rails::Application
auth_obj = described_class.auth_url(redirect_uri: "localhost:5000/another_callback")
expect(auth_obj[:url]).to match(/localhost%3A5000%2Fanother_callback/)
end

it "includes invitation_code and is_invitation when invitation_code is provided" do
auth_obj = described_class.auth_url(invitation_code: "test_invitation_123")
expect(auth_obj[:url]).to include("invitation_code=test_invitation_123")
expect(auth_obj[:url]).to include("is_invitation=true")
end

it "does not include is_invitation when invitation_code is not provided" do
auth_obj = described_class.auth_url
expect(auth_obj[:url]).not_to include("is_invitation")
expect(auth_obj[:url]).not_to include("invitation_code")
end

it "does not include is_invitation for empty invitation_code" do
auth_obj = described_class.auth_url(invitation_code: "")
expect(auth_obj[:url]).not_to include("is_invitation")
expect(auth_obj[:url]).not_to include("invitation_code")
end

it "does not include is_invitation for whitespace-only invitation_code" do
auth_obj = described_class.auth_url(invitation_code: " ")
expect(auth_obj[:url]).not_to include("is_invitation")
expect(auth_obj[:url]).not_to include("invitation_code")
end

it "includes invitation_code and is_invitation for valid invitation_code with whitespace" do
auth_obj = described_class.auth_url(invitation_code: " abc123 ")
expect(auth_obj[:url]).to include("invitation_code=abc123")
expect(auth_obj[:url]).to include("is_invitation=true")
end
end

describe "#logout_url" do
Expand Down