Skip to content

Commit

Permalink
Add more resilient storage capabilities with reference in-memory impl…
Browse files Browse the repository at this point in the history
…ementation.
  • Loading branch information
jhuckabee committed Dec 7, 2024
1 parent fd13c61 commit ca1fe3a
Show file tree
Hide file tree
Showing 43 changed files with 2,234 additions and 220 deletions.
19 changes: 17 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,26 @@ AllCops:
SuggestExtensions: false
TargetRubyVersion: 3.0

Metrics/BlockLength:
Enabled: false

Metrics/ClassLength:
Max: 500
Enabled: false

Metrics/ModuleLength:
Enabled: false

Metrics/MethodLength:
Max: 50
Enabled: false

Metrics/AbcSize:
Enabled: false

Metrics/PerceivedComplexity:
Enabled: false

Metrics/CyclomaticComplexity:
Enabled: false

Style/StringLiterals:
EnforcedStyle: double_quotes
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ source "https://rubygems.org"
gemspec

gem "minitest", "~> 5.25"
gem "minitest-reporters", "~> 1.7"
gem "mocha", "~> 2.6"
gem "mutex_m", "~> 0.3"
gem "rake", "~> 13.0"
Expand Down
8 changes: 8 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ GEM
specs:
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
ansi (1.5.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.8)
builder (3.3.0)
concurrent-ruby (1.3.4)
crack (1.0.0)
bigdecimal
Expand All @@ -30,6 +32,11 @@ GEM
language_server-protocol (3.17.0.3)
logger (1.6.2)
minitest (5.25.2)
minitest-reporters (1.7.1)
ansi
builder
minitest (>= 5.0)
ruby-progressbar
mocha (2.6.1)
ruby2_keywords (>= 0.0.5)
mutex_m (0.3.0)
Expand Down Expand Up @@ -79,6 +86,7 @@ PLATFORMS
DEPENDENCIES
atproto_auth!
minitest (~> 5.25)
minitest-reporters (~> 1.7)
mocha (~> 2.6)
mutex_m (~> 0.3)
rake (~> 13.0)
Expand Down
2 changes: 1 addition & 1 deletion bin/snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class SnapshotGenerator
file.puts(tree)
end

def generate_tree_structure(path, prefix = "") # rubocop:disable Metrics/AbcSize
def generate_tree_structure(path, prefix = "")
entries = Dir.entries(path).sort
entries.delete(".")
entries.delete("..")
Expand Down
58 changes: 46 additions & 12 deletions examples/confidential_client/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@

# Main app entry point
class ExampleApp < Sinatra::Base
def check_stored_session(session_id)
return false unless session_id

settings.oauth_client.authorized?(session_id)
rescue AtprotoAuth::SessionError
false
end

configure :development do
register Sinatra::Reloader
end
Expand Down Expand Up @@ -45,7 +53,28 @@ class ExampleApp < Sinatra::Base
)
end

helpers do
def recover_session
session_id = session[:oauth_session_id]
return nil unless session_id

begin
# Check if session is still valid
return nil unless settings.oauth_client.authorized?(session_id)

session_id
rescue AtprotoAuth::Client::SessionError
# Clear invalid session
session.delete(:oauth_session_id)
nil
end
end
end

get "/" do
# Check for existing session
redirect "/authorized" if recover_session

erb :index
end

Expand Down Expand Up @@ -78,7 +107,7 @@ class ExampleApp < Sinatra::Base
scope: "atproto"
)

# Store session ID for callback
# Store session ID in user's browser session
session[:oauth_session_id] = auth[:session_id]

# Redirect to authorization URL
Expand All @@ -98,8 +127,8 @@ class ExampleApp < Sinatra::Base
iss: params[:iss]
)

# Store tokens in session
session[:tokens] = result
# Store tokens
session[:oauth_session_id] = result[:session_id]

redirect "/authorized"
rescue StandardError => e
Expand All @@ -108,19 +137,18 @@ class ExampleApp < Sinatra::Base
end

# Show authorized state and test API call
get "/authorized" do # rubocop:disable Metrics/BlockLength
return redirect "/" unless session[:tokens]
get "/authorized" do
session_id = session[:oauth_session_id]
return redirect "/" unless check_stored_session(session_id)

begin
# Get current session
oauth_session = settings.oauth_client.get_tokens(session[:tokens][:session_id])
# Get current session tokens
oauth_session = settings.oauth_client.get_tokens(session_id)

# Check if token needs refresh (using default 30s buffer)
# Check if token needs refresh
if oauth_session[:expires_in] < 30
# Refresh token
oauth_session = settings.oauth_client.refresh_token(session[:tokens][:session_id])
# Update session with new tokens
session[:tokens] = oauth_session
oauth_session = settings.oauth_client.refresh_token(session_id)
end

# Make test API call to com.atproto.identity.resolveHandle
Expand All @@ -131,7 +159,7 @@ class ExampleApp < Sinatra::Base

# Get auth headers for request
headers = settings.oauth_client.auth_headers(
session_id: session[:tokens][:session_id],
session_id: session_id,
method: "GET",
url: "https://api.bsky.app/xrpc/com.atproto.identity.resolveHandle"
)
Expand All @@ -143,6 +171,7 @@ class ExampleApp < Sinatra::Base
end

@api_result = response.body
@session = oauth_session
erb :authorized
rescue StandardError => e
session[:error] = "API call failed: #{e.message}"
Expand All @@ -151,6 +180,11 @@ class ExampleApp < Sinatra::Base
end

get "/signout" do
if session[:oauth_session_id]
# Clean up stored session
settings.oauth_client.remove_session(session[:oauth_session_id])
end

session.clear
session[:notice] = "Successfully signed out"
redirect "/"
Expand Down
Empty file modified examples/confidential_client/scripts/generate_keys.rb
100644 → 100755
Empty file.
2 changes: 1 addition & 1 deletion examples/confidential_client/views/authorized.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<div class="token-info">
<p class="mb-1 text-gray-600 dark:text-gray-400 text-sm font-medium">Token Information</p>
<pre class="rounded-lg text-gray-700 dark:text-gray-100 bg-gray-100 dark:bg-slate-800 border border-gray-400 p-2 overflow-auto"><code><%= JSON.pretty_generate(session[:tokens]) %></code></pre>
<pre class="rounded-lg text-gray-700 dark:text-gray-100 bg-gray-100 dark:bg-slate-800 border border-gray-400 p-2 overflow-auto"><code><%= JSON.pretty_generate(@session) %></code></pre>
</div>

<div class="api-test">
Expand Down
27 changes: 26 additions & 1 deletion lib/atproto_auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@

require "atproto_auth/version"

require "atproto_auth/configuration"
require "atproto_auth/errors"
require "atproto_auth/configuration"
require "atproto_auth/encryption"
require "atproto_auth/client_metadata"
require "atproto_auth/http_client"
require "atproto_auth/pkce"

require "atproto_auth/storage/interface"
require "atproto_auth/storage/key_builder"
require "atproto_auth/storage/memory"

require "atproto_auth/server_metadata"
require "atproto_auth/server_metadata/origin_url"
require "atproto_auth/server_metadata/authorization_server"
Expand All @@ -36,6 +41,12 @@
require "atproto_auth/par/response"
require "atproto_auth/par/client"

require "atproto_auth/serialization/base"
require "atproto_auth/serialization/dpop_key"
require "atproto_auth/serialization/session"
require "atproto_auth/serialization/stored_nonce"
require "atproto_auth/serialization/token_set"

require "atproto_auth/token/refresh"

require "atproto_auth/client"
Expand All @@ -53,6 +64,20 @@ def configuration

def configure
yield(configuration)
configuration.validate!
configuration
end

# Gets the configured storage backend
# @return [Storage::Interface] The configured storage implementation
def storage
configuration.storage
end

# Resets the configuration to defaults
# Primarily used in testing
def reset_configuration!
@configuration = Configuration.new
end
end
end
Loading

0 comments on commit ca1fe3a

Please sign in to comment.