about summary refs log tree commit diff
path: root/src/state/session/util/index.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/session/util/index.ts')
-rw-r--r--src/state/session/util/index.ts177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/state/session/util/index.ts b/src/state/session/util/index.ts
new file mode 100644
index 000000000..e3e246f7b
--- /dev/null
+++ b/src/state/session/util/index.ts
@@ -0,0 +1,177 @@
+import {BSKY_LABELER_DID, BskyAgent} from '@atproto/api'
+import {jwtDecode} from 'jwt-decode'
+
+import {IS_TEST_USER} from '#/lib/constants'
+import {tryFetchGates} from '#/lib/statsig/statsig'
+import {hasProp} from '#/lib/type-guards'
+import {logger} from '#/logger'
+import * as persisted from '#/state/persisted'
+import {readLabelers} from '../agent-config'
+import {SessionAccount, SessionApiContext} from '../types'
+
+export function isSessionDeactivated(accessJwt: string | undefined) {
+  if (accessJwt) {
+    const sessData = jwtDecode(accessJwt)
+    return (
+      hasProp(sessData, 'scope') && sessData.scope === 'com.atproto.deactivated'
+    )
+  }
+  return false
+}
+
+export function readLastActiveAccount() {
+  const {currentAccount, accounts} = persisted.get('session')
+  return accounts.find(a => a.did === currentAccount?.did)
+}
+
+export function agentToSessionAccount(
+  agent: BskyAgent,
+): SessionAccount | undefined {
+  if (!agent.session) return undefined
+
+  return {
+    service: agent.service.toString(),
+    did: agent.session.did,
+    handle: agent.session.handle,
+    email: agent.session.email,
+    emailConfirmed: agent.session.emailConfirmed || false,
+    emailAuthFactor: agent.session.emailAuthFactor || false,
+    refreshJwt: agent.session.refreshJwt,
+    accessJwt: agent.session.accessJwt,
+    deactivated: isSessionDeactivated(agent.session.accessJwt),
+    pdsUrl: agent.pdsUrl?.toString(),
+  }
+}
+
+export function configureModerationForGuest() {
+  switchToBskyAppLabeler()
+}
+
+export async function configureModerationForAccount(
+  agent: BskyAgent,
+  account: SessionAccount,
+) {
+  switchToBskyAppLabeler()
+  if (IS_TEST_USER(account.handle)) {
+    await trySwitchToTestAppLabeler(agent)
+  }
+
+  const labelerDids = await readLabelers(account.did).catch(_ => {})
+  if (labelerDids) {
+    agent.configureLabelersHeader(
+      labelerDids.filter(did => did !== BSKY_LABELER_DID),
+    )
+  } else {
+    // If there are no headers in the storage, we'll not send them on the initial requests.
+    // If we wanted to fix this, we could block on the preferences query here.
+  }
+}
+
+function switchToBskyAppLabeler() {
+  BskyAgent.configure({appLabelers: [BSKY_LABELER_DID]})
+}
+
+async function trySwitchToTestAppLabeler(agent: BskyAgent) {
+  const did = (
+    await agent
+      .resolveHandle({handle: 'mod-authority.test'})
+      .catch(_ => undefined)
+  )?.data.did
+  if (did) {
+    console.warn('USING TEST ENV MODERATION')
+    BskyAgent.configure({appLabelers: [did]})
+  }
+}
+
+export function isSessionExpired(account: SessionAccount) {
+  try {
+    if (account.accessJwt) {
+      const decoded = jwtDecode(account.accessJwt)
+      if (decoded.exp) {
+        const didExpire = Date.now() >= decoded.exp * 1000
+        return didExpire
+      }
+    }
+  } catch (e) {
+    logger.error(`session: could not decode jwt`)
+  }
+  return true
+}
+
+export async function createAgentAndLogin({
+  service,
+  identifier,
+  password,
+  authFactorToken,
+}: {
+  service: string
+  identifier: string
+  password: string
+  authFactorToken?: string
+}) {
+  const agent = new BskyAgent({service})
+  await agent.login({identifier, password, authFactorToken})
+
+  const account = agentToSessionAccount(agent)
+  if (!agent.session || !account) {
+    throw new Error(`session: login failed to establish a session`)
+  }
+
+  const fetchingGates = tryFetchGates(account.did, 'prefer-fresh-gates')
+  await configureModerationForAccount(agent, account)
+
+  return {
+    agent,
+    account,
+    fetchingGates,
+  }
+}
+
+export async function createAgentAndCreateAccount({
+  service,
+  email,
+  password,
+  handle,
+  inviteCode,
+  verificationPhone,
+  verificationCode,
+}: Parameters<SessionApiContext['createAccount']>[0]) {
+  const agent = new BskyAgent({service})
+  await agent.createAccount({
+    email,
+    password,
+    handle,
+    inviteCode,
+    verificationPhone,
+    verificationCode,
+  })
+
+  const account = agentToSessionAccount(agent)!
+  if (!agent.session || !account) {
+    throw new Error(`session: createAccount failed to establish a session`)
+  }
+
+  const fetchingGates = tryFetchGates(account.did, 'prefer-fresh-gates')
+
+  if (!account.deactivated) {
+    /*dont await*/ agent.upsertProfile(_existing => {
+      return {
+        displayName: '',
+
+        // HACKFIX
+        // creating a bunch of identical profile objects is breaking the relay
+        // tossing this unspecced field onto it to reduce the size of the problem
+        // -prf
+        createdAt: new Date().toISOString(),
+      }
+    })
+  }
+
+  await configureModerationForAccount(agent, account)
+
+  return {
+    agent,
+    account,
+    fetchingGates,
+  }
+}