Class: Tempest::Session

Inherits:
Object
  • Object
show all
Defined in:
lib/tempest/session.rb

Constant Summary collapse

EXPIRY_LEEWAY_SECONDS =
30

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(access_jwt:, refresh_jwt:, did:, handle:, pds_host:, identifier: nil) ⇒ Session

Returns a new instance of Session.



42
43
44
45
46
47
48
49
50
# File 'lib/tempest/session.rb', line 42

def initialize(access_jwt:, refresh_jwt:, did:, handle:, pds_host:, identifier: nil)
  @access_jwt = access_jwt
  @refresh_jwt = refresh_jwt
  @did = did
  @handle = handle
  @pds_host = pds_host
  @identifier = identifier
  @refresh_mutex = Mutex.new
end

Instance Attribute Details

#access_jwtObject (readonly)

Returns the value of attribute access_jwt.



11
12
13
# File 'lib/tempest/session.rb', line 11

def access_jwt
  @access_jwt
end

#didObject (readonly)

Returns the value of attribute did.



11
12
13
# File 'lib/tempest/session.rb', line 11

def did
  @did
end

#handleObject (readonly)

Returns the value of attribute handle.



11
12
13
# File 'lib/tempest/session.rb', line 11

def handle
  @handle
end

#identifierObject

Returns the value of attribute identifier.



12
13
14
# File 'lib/tempest/session.rb', line 12

def identifier
  @identifier
end

#on_changeObject

Returns the value of attribute on_change.



12
13
14
# File 'lib/tempest/session.rb', line 12

def on_change
  @on_change
end

#pds_hostObject (readonly)

Returns the value of attribute pds_host.



11
12
13
# File 'lib/tempest/session.rb', line 11

def pds_host
  @pds_host
end

#refresh_jwtObject (readonly)

Returns the value of attribute refresh_jwt.



11
12
13
# File 'lib/tempest/session.rb', line 11

def refresh_jwt
  @refresh_jwt
end

Class Method Details

.create(config, auth_factor_token: nil) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/tempest/session.rb', line 14

def self.create(config, auth_factor_token: nil)
  url = "#{config.pds_host}/xrpc/com.atproto.server.createSession"
  body = { identifier: config.identifier, password: config.app_password }
  body[:authFactorToken] = auth_factor_token if auth_factor_token

  response = Tempest::HTTP.post_json(url, body: body)

  unless response.ok?
    details = response.body.is_a?(Hash) ? response.body : {}
    raise AuthenticationError.new(
      "createSession failed (#{response.status}): #{details["message"] || response.body.inspect}",
      code: details["error"],
    )
  end

  from_payload(response.body, pds_host: config.pds_host)
end

.from_payload(payload, pds_host:) ⇒ Object



32
33
34
35
36
37
38
39
40
# File 'lib/tempest/session.rb', line 32

def self.from_payload(payload, pds_host:)
  new(
    access_jwt: payload.fetch("accessJwt"),
    refresh_jwt: payload.fetch("refreshJwt"),
    did: payload.fetch("did"),
    handle: payload.fetch("handle"),
    pds_host: pds_host,
  )
end

Instance Method Details

#access_expired?Boolean

Returns:

  • (Boolean)


52
53
54
55
56
# File 'lib/tempest/session.rb', line 52

def access_expired?
  exp = jwt_exp(@access_jwt)
  return true if exp.nil?
  Time.now.to_i + EXPIRY_LEEWAY_SECONDS >= exp
end

#refresh!(if_unchanged_from: nil) ⇒ Object

Refreshes the session using the current refresh_jwt.

When ‘if_unchanged_from:` is supplied, the refresh is skipped if the session’s access_jwt has already moved past that value. Combined with the internal mutex, this lets concurrent callers coalesce a single refreshSession round-trip: the first caller refreshes while the rest wait for the lock and then observe the new token, no-op’ing instead of issuing duplicate refresh requests.



81
82
83
84
85
86
87
# File 'lib/tempest/session.rb', line 81

def refresh!(if_unchanged_from: nil)
  @refresh_mutex.synchronize do
    return self if if_unchanged_from && @access_jwt != if_unchanged_from

    perform_refresh
  end
end

#replace_with!(other) ⇒ Object

Adopts another Session’s credentials in place. Used by :relogin so the XRPCClient that already holds a reference to this Session keeps working without having to be reconstructed.



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/tempest/session.rb', line 61

def replace_with!(other)
  @refresh_mutex.synchronize do
    @access_jwt = other.access_jwt
    @refresh_jwt = other.refresh_jwt
    @did = other.did
    @handle = other.handle
    @pds_host = other.pds_host
  end
  @on_change&.call(self)
  self
end