Files
akkounts/spec/models/rs_authorization_code_spec.rb
T
raucao 295261b332
CI / Test (pull_request) Failing after 46s
Address review findings in the PKCE flow
- Make authorization-code redemption atomic: claim the code with a guarded
  delete inside the transaction that creates the authorization, so concurrent
  exchanges can't both issue a token (RFC 6749 4.1.3)
- Compare the token request's client_id host to the code's stored client_id
  (defense-in-depth alongside the exact redirect_uri match)
- Reject unknown response_type values with unsupported_response_type
- Validate the PKCE code_challenge (43-128 unreserved chars, S256) so a client
  can't persist an over-long or malformed challenge
- Return invalid_request for missing token-endpoint parameters, and set
  Cache-Control: no-store on error responses too
- Use the right separator when the redirect_uri already has a query string
- Look codes up through the active scope; drop the unused consume!; serialize
  permissions as JSON
- Purge abandoned authorization codes via a recurring Solid Queue task

Part of #254
2026-11-03 11:40:18 +04:00

63 lines
1.5 KiB
Ruby

require 'rails_helper'
RSpec.describe RsAuthorizationCode, type: :model do
let(:user) { create :user }
def build_code(**attrs)
RsAuthorizationCode.create!(
user: user,
client_id: "example.com",
redirect_uri: "https://example.com",
permissions: %w(documents:rw),
code_challenge: "challenge",
**attrs
)
end
describe "#create" do
it "generates a raw code and stores only its digest" do
code = build_code
expect(code.code).to be_present
expect(code.code_digest).to eq(Digest::SHA256.hexdigest(code.code))
end
it "sets a short expiry" do
code = build_code
expect(code.expires_at).to be_within(5.seconds).of(60.seconds.from_now)
end
end
describe ".find_by_code" do
it "finds a code by its raw value" do
code = build_code
expect(RsAuthorizationCode.find_by_code(code.code)).to eq(code)
end
it "returns nil for an unknown code" do
expect(RsAuthorizationCode.find_by_code("does-not-exist")).to be_nil
end
it "returns nil for a blank code" do
expect(RsAuthorizationCode.find_by_code(nil)).to be_nil
end
it "returns nil for an expired code" do
code = build_code(expires_at: 1.minute.ago)
expect(RsAuthorizationCode.find_by_code(code.code)).to be_nil
end
end
describe "#expired?" do
it "is false before the expiry" do
expect(build_code).not_to be_expired
end
it "is true after the expiry" do
expect(build_code(expires_at: 1.minute.ago)).to be_expired
end
end
end