Class: AtprotoAuth::Identity::Resolver

Inherits:
Object
  • Object
show all
Defined in:
lib/atproto_auth/identity/resolver.rb

Overview

Resolves and validates AT Protocol identities

Constant Summary collapse

PLC_DIRECTORY_URL =
"https://plc.directory"
DID_PLC_PREFIX =
"did:plc:"
HANDLE_REGEX =
/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/

Instance Method Summary collapse

Constructor Details

#initialize(plc_directory: nil) ⇒ Resolver

Creates a new Identity resolver

Parameters:

  • plc_directory (String) (defaults to: nil)

    Optional custom PLC directory URL



15
16
17
# File 'lib/atproto_auth/identity/resolver.rb', line 15

def initialize(plc_directory: nil)
  @plc_directory = plc_directory || PLC_DIRECTORY_URL
end

Instance Method Details

#get_did_info(did) ⇒ Hash

Fetches and parses DID Document

Parameters:

  • did (String)

    The DID to resolve

Returns:

  • (Hash)

    Resolution result with :did, :document, and :pds keys

Raises:



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/atproto_auth/identity/resolver.rb', line 41

def get_did_info(did)
  DID.new(did).validate!

  # Fetch and parse DID document
  doc_data = fetch_did_document(did)
  document = Document.new(doc_data)

  # Validate PDS URL format
  validate_pds_url!(document.pds)

  { did: did, document: document, pds: document.pds }
rescue DocumentError => e
  raise ResolutionError, "Invalid DID document: #{e.message}"
rescue StandardError => e
  raise ResolutionError, "Failed to resolve DID #{did}: #{e.message}"
end

#resolve_handle(handle) ⇒ Hash

Resolves a handle to a DID Document

Parameters:

  • handle (String)

    The handle to resolve (with or without @ prefix)

Returns:

  • (Hash)

    Resolution result with :did, :document, and :pds keys

Raises:



23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/atproto_auth/identity/resolver.rb', line 23

def resolve_handle(handle)
  validate_handle!(handle)
  normalized = normalize_handle(handle)

  # First try DNS-based resolution
  did = resolve_handle_dns(normalized)
  return get_did_info(did) if did

  # Fall back to HTTP resolution via a known PDS
  resolve_handle_http(normalized)
rescue StandardError => e
  raise ResolutionError, "Failed to resolve handle #{handle}: #{e.message}"
end

#verify_handle_binding(handle, did) ⇒ Boolean

Verifies that a handle belongs to a DID

Parameters:

  • handle (String)

    Handle to verify

  • did (String)

    DID to check against

Returns:

  • (Boolean)

    true if verification succeeds

Raises:



103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/atproto_auth/identity/resolver.rb', line 103

def verify_handle_binding(handle, did)
  info = get_did_info(did)

  unless info[:document].has_handle?(handle)
    raise ValidationError,
          "Handle #{handle} does not belong to DID #{did}"
  end

  true
rescue StandardError => e
  raise ValidationError, "Failed to verify handle binding: #{e.message}"
end

#verify_issuer_binding(did, issuer) ⇒ Boolean

Verifies that an auth server (issuer) is authorized for a DID

Parameters:

  • did (String)

    The DID to verify

  • issuer (String)

    The issuer URL to verify

Returns:

  • (Boolean)

    true if verification succeeds

Raises:



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/atproto_auth/identity/resolver.rb', line 79

def verify_issuer_binding(did, issuer)
  # Get PDS location from DID
  info = get_did_info(did)
  pds_url = info[:pds]

  # Fetch resource server metadata to find auth server
  resource_server = ServerMetadata::ResourceServer.from_url(pds_url)
  auth_server_url = resource_server.authorization_servers.first

  # Compare normalized URLs
  if normalize_url(auth_server_url) != normalize_url(issuer)
    raise ValidationError, "Issuer #{issuer} is not authorized for DID #{did}"
  end

  true
rescue StandardError => e
  raise ValidationError, "Failed to verify issuer binding: #{e.message}"
end

#verify_pds_binding(did, pds_url) ⇒ Boolean

Verifies that a PDS hosts a given DID

Parameters:

  • did (String)

    The DID to verify

  • pds_url (String)

    The PDS URL to check

Returns:

  • (Boolean)

    true if verification succeeds

Raises:



63
64
65
66
67
68
69
70
71
72
# File 'lib/atproto_auth/identity/resolver.rb', line 63

def verify_pds_binding(did, pds_url)
  info = get_did_info(did)
  if normalize_url(info[:pds]) != normalize_url(pds_url)
    raise ValidationError, "PDS #{pds_url} is not authorized for DID #{did}"
  end

  true
rescue StandardError => e
  raise ValidationError, "Failed to verify PDS binding: #{e.message}"
end