better_auth-stripe 0.6.2 → 0.8.0

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: d60c9b90345ede9ff5fda0bc9420f0f9d2cc415312e0a587daa1e2aa451ec109
4
- data.tar.gz: cdd8dfe8742cdc419eb0c94082ad9d5e30b9d6466bc938253cd54267e1e547fc
3
+ metadata.gz: a59a70c7091b88e1239c863a9a01997955d96920052cb1870b3e11736e48747a
4
+ data.tar.gz: 80b121e46ffaf456532e56b3c41e019f94358f59ed56e5c21356e2da036e6c9a
5
5
  SHA512:
6
- metadata.gz: ca83e6cbfc4c7ea4dc1f488ac3a91fe842a5fb19cfdd3754d9c334f65b9eb5e8ef1d01ba0c5c350f48a037a06a3e02665906d32f49b60a294878f2657ef0f66b
7
- data.tar.gz: 768570c25480a3549a1c85a795cfce82c7a0462ef8c15687619fd682c4aaa4ce9d3193e8490bc6496244ef1d88937a232ca6d3a8136b26223f199018755f02ff
6
+ metadata.gz: 2ba47732d4d4ed76aaa44a66792dc9f59d36c45c4155ab6043893cc31ed959424f6c14c4b776ab7fe98586f05fa57f5f61d8a9c6dc831cda73bb137c332598be
7
+ data.tar.gz: 191b1ed528b401dfd94a0626c2f7189d67bf44cfcd079cf183ea0874f33fc64e6abdc9187e87e4adf89c97aeecc32755cf42d8f26c417e7f32c6827b8c7013f6
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.7.0] - 2026-05-05
6
+
7
+ - Changed Stripe webhooks to reject requests when the configured Stripe client does not expose `webhooks.construct_event_async` or `webhooks.construct_event`, preventing unverified payload processing.
8
+ - Hardened Stripe subscription route middleware and webhook origin handling with regression coverage.
9
+
5
10
  ## [0.6.0] - 2026-05-02
6
11
 
7
12
  - Modularized the Stripe plugin into upstream-aligned client, schema, middleware, hooks, route, metadata, type, and utility modules while keeping the existing public facade.
@@ -47,6 +47,22 @@ module BetterAuth
47
47
 
48
48
  nil
49
49
  end
50
+
51
+ def validate_trusted_url!(ctx, value, label)
52
+ return if value.nil? || value.to_s.empty?
53
+
54
+ validation_value = value.to_s.gsub("{CHECKOUT_SESSION_ID}", "checkout_session_id")
55
+ return if ctx.context.trusted_origin?(validation_value, allow_relative_paths: true)
56
+
57
+ raise APIError.new("FORBIDDEN", message: "Invalid #{label}")
58
+ end
59
+
60
+ def validate_trusted_urls!(ctx, source, mapping)
61
+ body = BetterAuth::Plugins.normalize_hash(source || {})
62
+ mapping.each do |key, label|
63
+ validate_trusted_url!(ctx, body[key], label)
64
+ end
65
+ end
50
66
  end
51
67
  end
52
68
  end
@@ -10,6 +10,7 @@ module BetterAuth
10
10
  BetterAuth::Endpoint.new(path: "/subscription/cancel", method: "POST") do |ctx|
11
11
  session = BetterAuth::Routes.current_session(ctx)
12
12
  body = BetterAuth::Plugins.normalize_hash(ctx.body)
13
+ BetterAuth::Stripe::Middleware.validate_trusted_urls!(ctx, body, return_url: "returnUrl")
13
14
  customer_type = BetterAuth::Plugins.stripe_customer_type!(body)
14
15
  reference_id = BetterAuth::Plugins.stripe_reference_id!(ctx, session, customer_type, body[:reference_id], config)
15
16
  BetterAuth::Plugins.stripe_authorize_reference!(ctx, session, reference_id, "cancel-subscription", customer_type, BetterAuth::Plugins.stripe_subscription_options(config), explicit: body.key?(:reference_id))
@@ -10,6 +10,7 @@ module BetterAuth
10
10
  BetterAuth::Endpoint.new(path: "/subscription/cancel/callback", method: "GET") do |ctx|
11
11
  query = BetterAuth::Plugins.normalize_hash(ctx.query)
12
12
  callback = query[:callback_url] || "/"
13
+ BetterAuth::Stripe::Middleware.validate_trusted_url!(ctx, callback, "callbackURL")
13
14
  unless query[:subscription_id]
14
15
  raise ctx.redirect(BetterAuth::Plugins.stripe_url(ctx, callback))
15
16
  end
@@ -10,6 +10,7 @@ module BetterAuth
10
10
  BetterAuth::Endpoint.new(path: "/subscription/billing-portal", method: "POST") do |ctx|
11
11
  session = BetterAuth::Routes.current_session(ctx)
12
12
  body = BetterAuth::Plugins.normalize_hash(ctx.body)
13
+ BetterAuth::Stripe::Middleware.validate_trusted_urls!(ctx, body, return_url: "returnUrl")
13
14
  customer_type = BetterAuth::Plugins.stripe_customer_type!(body)
14
15
  reference_id = BetterAuth::Plugins.stripe_reference_id!(ctx, session, customer_type, body[:reference_id], config)
15
16
  BetterAuth::Plugins.stripe_authorize_reference!(ctx, session, reference_id, "billing-portal", customer_type, BetterAuth::Plugins.stripe_subscription_options(config), explicit: body.key?(:reference_id))
@@ -7,23 +7,29 @@ module BetterAuth
7
7
  module_function
8
8
 
9
9
  def endpoint(config)
10
- BetterAuth::Endpoint.new(path: "/stripe/webhook", method: "POST") do |ctx|
10
+ BetterAuth::Endpoint.new(path: "/stripe/webhook", method: "POST", disable_body: true) do |ctx|
11
11
  signature = ctx.headers["stripe-signature"]
12
12
  raise BetterAuth::APIError.new("BAD_REQUEST", message: BetterAuth::Stripe::ERROR_CODES.fetch("STRIPE_SIGNATURE_NOT_FOUND")) if signature.to_s.empty?
13
13
 
14
14
  raise BetterAuth::APIError.new("INTERNAL_SERVER_ERROR", message: BetterAuth::Stripe::ERROR_CODES.fetch("STRIPE_WEBHOOK_SECRET_NOT_FOUND")) if config[:stripe_webhook_secret].to_s.empty?
15
15
 
16
+ payload = ctx.raw_body.to_s.empty? ? ctx.body : ctx.raw_body
16
17
  event = begin
17
- if BetterAuth::Plugins.stripe_client(config).respond_to?(:webhooks)
18
- webhooks = BetterAuth::Plugins.stripe_client(config).webhooks
19
- if webhooks.respond_to?(:construct_event_async)
20
- webhooks.construct_event_async(ctx.body, signature, config[:stripe_webhook_secret])
21
- else
22
- webhooks.construct_event(ctx.body, signature, config[:stripe_webhook_secret])
23
- end
18
+ client = BetterAuth::Plugins.stripe_client(config)
19
+ webhooks = client.webhooks if client.respond_to?(:webhooks)
20
+ unless webhooks
21
+ raise BetterAuth::APIError.new("BAD_REQUEST", message: BetterAuth::Stripe::ERROR_CODES.fetch("FAILED_TO_CONSTRUCT_STRIPE_EVENT"))
22
+ end
23
+
24
+ if webhooks.respond_to?(:construct_event_async)
25
+ webhooks.construct_event_async(payload, signature, config[:stripe_webhook_secret])
26
+ elsif webhooks.respond_to?(:construct_event)
27
+ webhooks.construct_event(payload, signature, config[:stripe_webhook_secret])
24
28
  else
25
- ctx.body
29
+ raise BetterAuth::APIError.new("BAD_REQUEST", message: BetterAuth::Stripe::ERROR_CODES.fetch("FAILED_TO_CONSTRUCT_STRIPE_EVENT"))
26
30
  end
31
+ rescue BetterAuth::APIError
32
+ raise
27
33
  rescue
28
34
  raise BetterAuth::APIError.new("BAD_REQUEST", message: BetterAuth::Stripe::ERROR_CODES.fetch("FAILED_TO_CONSTRUCT_STRIPE_EVENT"))
29
35
  end
@@ -10,6 +10,7 @@ module BetterAuth
10
10
  BetterAuth::Endpoint.new(path: "/subscription/success", method: "GET") do |ctx|
11
11
  query = BetterAuth::Plugins.normalize_hash(ctx.query)
12
12
  callback = query[:callback_url] || "/"
13
+ BetterAuth::Stripe::Middleware.validate_trusted_url!(ctx, callback, "callbackURL")
13
14
  checkout_session_id = query[:checkout_session_id]
14
15
  subscription_id = query[:subscription_id]
15
16
  if checkout_session_id
@@ -10,6 +10,7 @@ module BetterAuth
10
10
  BetterAuth::Endpoint.new(path: "/subscription/upgrade", method: "POST") do |ctx|
11
11
  session = BetterAuth::Routes.current_session(ctx)
12
12
  body = BetterAuth::Plugins.normalize_hash(ctx.body)
13
+ BetterAuth::Stripe::Middleware.validate_trusted_urls!(ctx, body, success_url: "successUrl", cancel_url: "cancelUrl", return_url: "returnUrl")
13
14
  subscription_options = BetterAuth::Plugins.stripe_subscription_options(config)
14
15
  customer_type = BetterAuth::Plugins.stripe_customer_type!(body)
15
16
  reference_id = BetterAuth::Plugins.stripe_reference_id!(ctx, session, customer_type, body[:reference_id], config)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module BetterAuth
4
4
  module Stripe
5
- VERSION = "0.6.2"
5
+ VERSION = "0.8.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_auth-stripe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Sala
@@ -100,7 +100,8 @@ dependencies:
100
100
  - !ruby/object:Gem::Version
101
101
  version: '1.0'
102
102
  description: Adds Stripe customer, subscription, checkout, billing portal, and webhook
103
- routes for Better Auth Ruby.
103
+ routes for Better Auth Ruby. Better Auth Ruby is an independent modern authentication
104
+ framework for Ruby inspired by Better Auth.
104
105
  email:
105
106
  - sebastian.sala.tech@gmail.com
106
107
  executables: []